【问题标题】:Why is this not a call of a pure virtual function?为什么这不是纯虚函数的调用?
【发布时间】:2015-08-27 02:41:20
【问题描述】:

我尝试“修复”this answer 中的示例,以演示如何调用纯虚函数。

#include <iostream>
using namespace std;

class A
{
    int id;
public:
    A(int i): id(i) {}
    int callFoo() { return foo(); }
    virtual int foo() = 0;
};

class B: public A
{
public:
    B(): A(callFoo()) {}
    int foo() { return 3; }
};

int main() {
    B b; // <-- this should call a pure virtual function
    cout << b.callFoo() << endl;
    return 0;
}

但我没有收到运行时错误here (with C++ 4.9.2),而是输出 3。我在 Borland C++ 5.6.4 上尝试了相同的操作,但我遇到了访问冲突。我认为foo()在基类的构造函数的调用中应该是纯虚的。

谁错了?我应该尝试更多的编译器吗?我对虚函数的理解正确吗?

【问题讨论】:

  • 我不会使用 Borland C++ 的测试结果来查看代码的 sn-p 是否有效和/或是否符合标准;)
  • @CoryKramer 虚拟功能已经使用了几十年。
  • 我知道,我在对仍在使用 Borland IDE 的人发表厚颜无耻的评论
  • ;-) 仅当我必须时,可悲的是,情况仍然如此。
  • 我认为你在以未定义的行为跳舞。如果您查看this example,我会在将cout 语句添加到A::callFoo() 时遇到分段错误。如果我们删除 cout 语句,那么它会编译并工作。我不得不猜测,当指针保持对齐时,最小的例子恰好可以工作。

标签: c++ pure-virtual


【解决方案1】:

您的代码具有未定义的行为:在对象的所有基类都被初始化之前调用对象(甚至是非虚拟对象)的成员函数是 UB。 C++14 (n4140) 12.6.2/14,强调我的:

可以为正在构建的对象调用成员函数(包括虚拟成员函数,10.3)。 同样,正在构建的对象可以是 typeid 运算符 (5.2.8) 或 dynamic_cast (5.2.7) 的操作数。 但是,如果这些操作在 ctor-initializer 中执行(或在直接调用的函数中 或间接从 ctor-initializer) 在所有基类的 mem-initializers 完成之前,结果 的操作是未定义的。 ...

ctor-initializer: 之后的整个列表。 mem-initializer 是这个列表的一个元素。

【讨论】:

  • 是的,这个 UB 规则是有根据的。但这只会让人头疼 - 没有编译器或静态代码检查器会发现所有错误。我很震惊,如此简单的内联案例没有立即被拒绝。
  • @Wolf:编译器is not required to perform a full control-flow analysis,因为这可能无法在编译时静态完成。
  • @Kevin 实现纯虚函数以摆脱 pure virtual function called 消息对我来说似乎是一种滥用,或者它正是为此目的而设计的?
  • @Wolf:我不提倡在那篇文章中讨论的修复,我只是认为它包含一些有趣的信息。我对实现纯虚函数持谨慎态度,因为它可能会使某些人感到困惑。
【解决方案2】:

语句B b; 将默认构造函数调用为B

在构造 B 时,在完全构造 A 之前,不会构造任何与 B 相关的内容。

因此,在尝试调用 callFoo() 时,行为是未定义的,因为您不能依赖 v-table 来设置类 B

总而言之:在抽象类的构造过程中调用纯虚函数的行为是未定义的。

【讨论】:

  • 我猜会是这样,因为。所以,我的“修复”建议似乎有点懒惰。
猜你喜欢
  • 1970-01-01
  • 2014-08-05
  • 2010-11-07
  • 2012-07-01
  • 2011-08-06
  • 2011-06-04
  • 1970-01-01
  • 2012-12-16
相关资源
最近更新 更多