C++ 构造/析构函数中调用虚函数的问题

       参加某公司的面试,遇到一个很有趣的问题,在构造/析构函数中调用虚函数,当时没答出来,回来查了一下资料,整理如下:

测试代码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的错误。编译器不会绕弯啊。


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张之海

若有帮助,客官打赏一分吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值