Paul_xxb

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
       参加某公司的面试,遇到一个很有趣的问题,在构造/析构函数中调用虚函数,当时没答出来,回来查了一下资料,整理如下:

测试代码1:

#include "stdafx.h"

using namespace std; 

class ClassA
{
public:
	ClassA(){
		cout<<"ClassA::ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::ClassA() end"<<endl;;
	}
	virtual void Print(){
		cout<<"ClassA::Print()"<<endl;
	}
	virtual ~ClassA(){
		cout<<"ClassA::~ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::~ClassA() end"<<endl;;
	}
};

class ClassB : public ClassA
{
public:
	ClassB(){
		cout<<"ClassB::ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::ClassB() end"<<endl;;
	}

	void Print(){
		cout<<"ClassB::Print()"<<endl;
	}

	~ClassB(){
		cout<<"ClassB::~ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::~ClassB() end"<<endl;;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	ClassB* pClassB = new ClassB;

	cout<<"------------"<<endl;
	ClassA* pClassA = pClassB;
	pClassA->Print();

	cout<<"------------"<<endl;
	delete pClassB;

	getchar();

	return 0;
}


在vs2003运行结果如下

ClassA::ClassA() begin
ClassA::Print()
ClassA::ClassA() end
ClassB::ClassB() begin
ClassB::Print()
ClassB::ClassB() end
------------
ClassB::Print()
------------
ClassB::~ClassB() begin
ClassB::Print()
ClassB::~ClassB() end
ClassA::~ClassA() begin
ClassA::Print()
ClassA::~ClassA() end

(对代码稍作修改,在gcc中的编译结果也是如此)

        可以看到,在new ClassB时,虽然在父类ClassA的构造函数调了的是被ClassB覆盖的虚函数Print(),但是实际上还是调用的ClassA的Print。这是因为

        继承类在构造的时候总是首先调用其基类的构造函数来对属于其基类的部分进行构造,在这个时候,整个类被当作基类来处理,继承类的部分对整个类来说好像不存在一样,直到基类的构造函数退出并进入继承类的构造函数,该类才被当作继承类来出来处理。对析构也一样,只是析构的顺序正好相反。

       进一步分析,如果在析构函数中调用纯虚函数呢?将ClassA中的Print()改为纯虚函数

测试代码2:

#include "stdafx.h" 

using namespace std; 

class ClassA
{
public:
	ClassA(){
		cout<<"ClassA::ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::ClassA() end"<<endl;;
	}
	virtual	void Print() = 0;
	virtual ~ClassA(){
		cout<<"ClassA::~ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::~ClassA() end"<<endl;;
	}
};

class ClassB : public ClassA
{
public:
	ClassB(){
		cout<<"ClassB::ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::ClassB() end"<<endl;;
	}

	void Print(){
		cout<<"ClassB::Print()"<<endl;
	}

	~ClassB(){
		cout<<"ClassB::~ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::~ClassB() end"<<endl;;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	ClassB* pClassB = new ClassB;

	cout<<"------------"<<endl;
	ClassA* pClassA = pClassB;
	pClassA->Print();

	cout<<"------------"<<endl;
	delete pClassB;

	getchar();

	return 0;
}

        编译出错:

Test error LNK2019: 无法解析的外部符号 "public: virtual void __thiscall ClassA::Print(void)" (?Print@ClassA@@UAEXXZ) ,该符号在函数 "public: __thiscall ClassA::ClassA(void)" (??0ClassA@@QAE@XZ) 中被引用

        gcc中也有类似提示


        稍加改动,继续测试:

测试代码3:

#include "stdafx.h"

using namespace std; 

class ClassA
{
public:
	ClassA(){
		cout<<"ClassA::ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::ClassA() end"<<endl;;
	}
	virtual	void Print(){
		Print2();
	}
	virtual	void Print2() = 0;
	virtual	~ClassA(){
		cout<<"ClassA::~ClassA() begin"<<endl;
		Print();
		cout<<"ClassA::~ClassA() end"<<endl;;
	}
};

class ClassB : public ClassA
{
public:
	ClassB(){
		cout<<"ClassB::ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::ClassB() end"<<endl;;
	}

	void Print(){
		cout<<"ClassB::Print()"<<endl;
	}
	void Print2(){
		cout<<"ClassB::Print2()"<<endl;
	}
	~ClassB(){
		cout<<"ClassB::~ClassB() begin"<<endl;
		Print();
		cout<<"ClassB::~ClassB() end"<<endl;;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	ClassB* pClassB = new ClassB;

	cout<<"------------"<<endl;
	ClassA* pClassA = pClassB;
	pClassA->Print();

	cout<<"------------"<<endl;
	delete pClassB;

	getchar();

	return 0;
}

           成功编译,但是运行时出现runtime error的错误。编译器不会绕弯啊。


        最后,建议大家在构造函数中别做太复杂的事,最好只是对成员变量的初始化工作。复杂点的操作另写一个初始化函数。

       

作者:xiaoxibo 发表于2011-7-18 2:07:17 原文链接
阅读:29 评论:1 查看评论
posted on 2011-07-18 02:07  Paul_xxb  阅读(4813)  评论(0编辑  收藏  举报