zoukankan      html  css  js  c++  java
  • 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的错误。编译器不会绕弯啊。


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

           

    作者:xiaoxibo 发表于2011-7-18 2:07:17 原文链接
    阅读:29 评论:1 查看评论
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 奇偶判断
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Qt: 访问容器(三种方法,加上for循环就四种了)good
  • 原文地址:https://www.cnblogs.com/xiaoxibo/p/2212612.html
Copyright © 2011-2022 走看看