tom*_*tom 6 c++ constructor virtual-functions
我正在阅读有效的C++,并且有"第9项:在构造或销毁期间从不调用虚函数".而且我想知道我的代码是否正常,即使它违反了这个规则:
using namespace std;
class A{
public:
A(bool doLog){
if(doLog)
log();
}
virtual void log(){
cout << "logging A\n";
}
};
class B: public A{
public:
B(bool doLog) : A(false){
if(doLog)
log();
}
virtual void log(){
cout << "logging B\n";
}
};
int main() {
A a(true);
B b(true);
}
Run Code Online (Sandbox Code Playgroud)
这种方法有问题吗?当我做一些更复杂的事情时,我可能遇到麻烦吗?
它接近我,大多数答案都没有得到我在那里做的,他们只是再次解释为什么从构造函数调用虚函数可能有危险.
我想强调一下我的程序输出如下:
logging A
logging B
Run Code Online (Sandbox Code Playgroud)
因此,我在构造时记录A并在构造时记录B.这就是我想要的!但我问你是否发现任何错误(有潜在危险)与我的"黑客"克服在构造函数中调用虚函数的问题.
tao*_*ocp 15
这种方法有问题吗?
Bjarne Stroustrup的回答:
我可以从构造函数中调用虚函数吗?
是的,但要小心.它可能没有你所期望的.在构造函数中,虚拟调用机制被禁用,因为尚未发生从派生类的重写.对象是从基础构建的,"在派生之前的基础".考虑:
Run Code Online (Sandbox Code Playgroud)#include<string> #include<iostream> using namespace std; class B { public: B(const string& ss) { cout << "B constructor\n"; f(ss); } virtual void f(const string&) { cout << "B::f\n";} }; class D : public B { public: D(const string & ss) :B(ss) { cout << "D constructor\n";} void f(const string& ss) { cout << "D::f\n"; s = ss; } private: string s; }; int main() { D d("Hello"); }
该程序编译和生产
Run Code Online (Sandbox Code Playgroud)B constructor B::f D constructor
注意不是D :: f.考虑如果规则不同会发生什么,以便从B :: B()调用D :: f():因为构造函数D :: D()尚未运行,D :: f()会尝试将其参数分配给未初始化的字符串s.结果很可能是立即崩溃.销毁是在"基类之前的派生类"完成的,因此虚函数的行为与构造函数相同:只使用本地定义 - 并且不会调用重写函数以避免触及对象的(现在销毁的)派生类部分.
有关详细信息,请参阅D&E 13.2.4.2或TC++ PL3 15.4.3.
有人建议此规则是实现工件.不是这样.事实上,实现从构造函数调用虚函数的不安全规则与从其他函数完全相同,会明显更容易.但是,这意味着不能编写任何虚函数来依赖基类建立的不变量.那将是一个糟糕的混乱.
And*_*owl 11
而且我想知道即使它违反了这个规则我的代码是否正常:
这取决于你的意思是"罚款".您的程序格式正确,其行为定义明确,因此不会调用未定义的行为和类似的东西.
但是,当看到对虚函数的调用时,可以预期通过调用覆盖该函数的最派生类型提供的实现来解析调用.
除了在构造期间,相应的子对象尚未构建,因此最派生的子对象是当前正在构造的子对象.结果:调用调用,就像函数不是虚拟的一样.
这是违反直觉的,您的程序不应该依赖于此行为.因此,作为一名有文化的程序员,您应该习惯于避免这种模式并遵循Scott Meyer的指导方针.