如果在没有限定的情况下从构造函数/析构函数调用虚拟函数,则是否会发生虚拟调度
If a virtual function is called from a constructor/destructor without qualification, does virtual dispatch occur?
struct A
{
virtual ~A() { f(); }
virtual void f() {}
};
我已经编辑了我的问题以更具体..
在此代码示例中,调用是否可以f()
使用虚拟调度,或者是否保证等同于 A::f()
?
您能提供C++标准中的相关部分吗?谢谢。
在构造函数或析构函数中,子类对象尚未构造或已被销毁。因此,虚拟调度不会导致使用派生类版本,而是调用基类版本。
从标准来看,[class.cdtor]/4
:
成员函数,包括虚函数 (10.3(,可以在构造或销毁 (12.6.2( 期间调用。当从构造函数或析构函数直接或间接调用虚函数时,包括在构造或销毁类的非静态数据成员期间,并且调用适用的对象是正在构造或销毁的对象(称为 x(,则调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生更多类中重写它的函数。如果虚拟函数调用使用显式类成员访问 (5.2.5(,并且对象表达式引用 x 的完整对象或该对象的基类子对象之一,但不是 x 或其基类子对象之一,则行为是未定义的。
举了一个例子:
struct V {
virtual void f();
virtual void g();
};
struct A : virtual V {
virtual void f();
};
struct B : virtual V {
virtual void g();
B(V*, A*);
};
struct D : A, B {
virtual void f();
virtual void g();
D() : B((A*)this, this) { }
};
B::B(V* v, A* a) {
f(); // calls V::f, not A::f
g(); // calls B::g, not D::g
v->g(); // v is base of B, the call is well-defined, calls B::g
a->f(); // undefined behavior, a’s type not a base of B
}
另请注意,如果调用的函数是纯虚拟函数,则这可能是不安全的,从 [class.abstract]/6
成员函数可以从抽象类的构造函数(或析构函数(调用;对于从此类构造函数(或析构函数(创建(或销毁(的对象,直接或间接对纯虚函数进行虚拟调用(10.3(的效果是不确定的。
该标准不要求动态或静态执行调用。从概念上讲,它是动态的,并且在构造或销毁对象时有关调用虚函数的所有引号都直接或间接包含文本。这很重要,因为它意味着无论调用是否直接针对函数,行为都应该相同。现在考虑:
struct A {
A() { f(); }
void f() { g(); }
virtual void g() {};
};
并假设代码不可见以及所有常见的警告,以便函数不内联。A::f()
的定义可能在不同的翻译单元中,不知道它是从构造函数还是析构函数调用的,或者两者都不是。它不知道完整的对象是A
类型还是Z
派生类型Z
,所以它必须使用动态调度。
现在,as-if 规则意味着编译器有一些优化的余地,它可以决定在构造函数/析构函数(或内联到其中的任何函数(的主体中,最终覆盖器是已知的,因此它可以避免动态调度并直接调用已知的最终覆盖器。这甚至适用于纯虚函数,因为在这种情况下,行为是未定义的,因此无法保证行为 - 因此编译器的任何转换在这种情况下都是有效的。
§12.7.4:
成员函数,包括虚函数 (10.3(,可以在构造或销毁 (12.6.2( 期间调用。 当从构造函数或析构函数直接或间接调用虚函数时,包括在构造或销毁类的非静态数据成员期间,并且调用适用的对象是正在构造或销毁的对象(称为 x(,则调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生更多类中重写它。
§10.4.6:
成员函数可以从抽象类的构造函数(或析构函数(调用;对于从此类构造函数(或析构函数(创建(或销毁(的对象,直接或间接对纯虚函数进行虚拟调用(10.3(的效果是不确定的。
发生虚拟调度,调用的函数是类链中的最后一个覆盖,直到被构造的覆盖。如果从未实现该函数,则行为未定义。
// well-defined behavior, calls A::f()
struct A
{
A() { f(); }
virtual void f();
};
struct B
{
virtual void f();
};
// well-defined behavior, calls B::f()
struct C : public B
{
C() { f(); }
};
// well-defined behavior, calls D::f()
struct D : public B
{
D() { f(); }
virtual void f();
};
// calling a pure virtual method from a constructor: undefined behavior
// (even if D::f() is implemented at a later point)
struct D
{
D() { f(); }
virtual void f() = 0;
};
// specifying f() as pure virtual even if it has an implementation in B,
// then calling it from the constructor: undefined behavior
struct E : public B
{
E() { f(); }
virtual void f() = 0;
};
是的。从其类中对成员的任何非限定引用完全等效于
this->member
或
this->member(...)
如果合适,因此如果是虚拟功能,则以虚拟方式发送。该标准不会对来自构造函数或析构函数的调用做出任何例外。它确实对调用哪个函数进行了例外,但对如何完成没有例外。
编辑
用于实现此异常的实际 VFT 机制在 [1] 中描述。正如 Lippman指出的那样,简单地擦除虚拟 despath 不是一种可接受的技术,因为被调用的虚函数调用的任何间接虚函数调用也受到相同的例外("直接或间接"条款(的约束。
[1] Lippman, Stanley B., *Inside the C++ Object Model,* Addison Wesley 1996, pp179ff.
- C++无法定义虚拟函数 OUTER 类和头文件
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 类型擦除的std::function与虚拟函数调用的开销
- 重写虚拟函数和继承
- 用纯虚拟函数兜圈子
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 禁止子函数调用父级的抽象(或虚拟)函数
- 无法在子类中使用虚拟函数C++
- 无法在派生对象上运行虚拟函数
- 我可以调用从 main() 覆盖的虚拟函数吗?
- 在 C++ 中将函数获取和设置为虚拟函数
- 使用在堆栈上创建的对象调用虚拟函数
- 为什么在这种情况下不调用我的虚拟函数实现?
- 在C++中使虚拟函数私有化
- 模板继承类中的虚拟函数
- 为什么构造函数的虚拟函数调用有时有效,但其他调用却无效
- doxygenc++虚拟函数和实现
- 如何从派生类函数中调用虚拟函数