C++编程经验:不要在构造函数和析构函数中使用虚函数

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++编程经验:不要在构造函数和析构函数中使用虚函数相关的知识,希望对你有一定的参考价值。

这个事情呢,其实我们平时也不会去做的,对吧。
当然要是做了的话,那也可以做好某些天连夜加班的准备。


构造函数中调用虚函数

首先构造函数中不能调用虚函数,不是说语法不允许,最重要的原因在于,当有继承的时候,父类会调用到子类的函数,但是此时子类并没有初始化,会导致数据错误,就这一点足已让你不能在构造函数中调用虚函数。

在基类的构造过程中,虚函数调用从不会被传递到派生类中。代之的是,派生类对象表现出来的行为好象其本身就是基类型。不规范地说,在基类的构造过程中,虚函数并没有被"构造"。

简单的说就是,在子类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。

对上面这种看上去有点违背直觉的行为可以用一个理由来解释:因为基类构造器是在派生类之前执行的,所以在基类构造器运行的时候派生类的数据成员还没有被初始化。如果在基类的构造过程中对虚函数的调用传递到了派生类, 派生类对象当然可以参照引用局部的数据成员,但是这些数据成员其时尚未被初始化。这将会导致无休止的未定义行为和彻夜的代码调试。沿类层次往下调用尚未初始化的对象的某些部分本来就是危险的,所以C++干脆不让你这样做。

#include <iostream>
using namespace std;
class A
{
public:
	A()
	{ 
		cout << "A构造函数";
		Test();
	}
	~A()
	{
		cout << "A析构函数";
		cout << "A::Test()" << endl;
	}
	virtual void Test()
	{
		cout << "A::Test()" << endl;
	}
};
 
class B:public A
{
public:
	B()
	{
		cout << "B构造函数";
		Test();
	}
	~B()
	{
		cout << "B析构函数";
		Test();
	}
	virtual void Test()
	{
		cout << "B::Test()" << endl;
	}
};
 
int main()
{
	A* pA = new B();
	cout << "动态调用:";
	pA->Test();
 
	delete pA;
	return 0;
}

对于这段代码,可能会执行出结果来,但是不要过于庆幸,新版的编译器会拒绝你在构造函数中调用了虚函数,然后帮你调整了方向。。。
因为编译器不这么做就导致你不确定的数据错误。

对于这个问题,看到一个很nice的说法:在一些平台正常,在另一些平台未必正常。今天正常,以后未必正常


析构函数中调用虚函数

在对象的析构期间,存在与上面同样的逻辑。一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。

以上是关于C++编程经验:不要在构造函数和析构函数中使用虚函数的主要内容,如果未能解决你的问题,请参考以下文章

第八章:不要在构造和析构函数中使用虚函数

构造函数和析构函数可以是虚函数吗

构造函数和析构函数可以是虚函数吗

小白学习C++ 教程十二C++面向对象编程中的构造函数和析构函数

C++中使用构造函数和析构函数的对象计数器

c++ 复制构造函数和析构函数