我有一些代码,我真的想从构造函数中调用虚方法。我知道这被认为是不安全的,我对对象构造了解得足以理解why。我也没有遇到these problems。目前我的代码正在运行,我认为它应该没问题,但我想确定。
这是我正在做的事情:
我有一些类层次结构,并且有一个普通的公共函数,它像往常一样转发到私有虚方法。但是我确实希望在构造我的对象时调用这个公共方法,因为它将所有数据填充到对象中。我绝对肯定这个虚拟调用来自叶类,因为从类层次结构的任何其他部分使用这个虚方法根本就没有意义。
所以在我看来,一旦我进行虚拟调用就应该完成对象创建,一切都应该没问题。还有什么可能出错吗?我想我必须用一些重要的注释来标记逻辑的这一部分,以解释为什么这个逻辑永远不会被移动到任何基本条目,即使看起来它可能被移动。但除了其他程序员的愚蠢之外,我应该没事,不应该吗?
答案 0 :(得分:5)
在构造函数或析构函数中调用任何非抽象虚函数是绝对安全的!但是,它的行为可能会令人困惑,因为它可能无法实现预期。在执行类的构造函数时,对象的静态和动态类型是构造函数的类型。也就是说,虚拟函数将永远分派给另一个派生类的覆盖。除此之外,虚拟调度实际上有效:例如当通过基类指针或引用调用虚函数时,正确调度到当前构造函数或被破坏的类中的覆盖。例如(可能是拼写错误,因为我目前无法使用此代码):
#include <iostream>
struct A {
virtual ~A() {}
virtual void f() { std::cout << "A::f()\n"; }
void g() { this->f(); }
};
struct B: A {
B() { this->g(); } // this prints 'B::f()'
void f() { std::cout << "B::f()\n"; }
};
struct C: B {
void f() { std::cout << "C::f()\n"; } // not called from B::B()
};
int main() {
C c;
}
也就是说,如果您不希望将虚函数调度到另一个派生函数,则可以在构造函数或类的析构函数中直接或间接调用虚函数。你甚至可以这样做,只要它被定义,虚拟函数在给定的类中是抽象的。但是,调度未定义的抽象函数会导致运行时错误。
答案 1 :(得分:3)
当调用构造函数时,该类被设置为该类的实例,但不是派生类。您不能从基础构造函数调用派生类的虚函数。当你到达派生类最多的构造函数时,所有虚函数都应该可以安全地调用。
如果您希望确保某人不能进行不正确的调用,请在基类中定义虚函数,并在调用它时断言和/或抛出异常。
答案 2 :(得分:2)
规则并不是因为您需要在叶子类中才能意识到当您从Foo::Foo(..)
进行成员调用时,该对象恰好是Foo
,即使它是在成为Bar
的过程中(假设Foo
来自Bar
并且您正在构建Bar
实例)。这是100%可靠的。
否则,该成员是虚拟的事实并不那么重要。还有其他陷阱与非虚函数同样发生:如果你要调用虚拟或非虚方法,假设对象是完全构造的,但在构造函数中调用它之前就是这种情况,你也会有问题。这些只是难以确定的情况,因为不仅你所调用的功能必须正常,它所调用的所有功能都必须正常。
这听起来不像你有问题,只是其中一个容易出错的地方。