Effective c++学习笔记——条款07:为多态基类声明virtual析构函数

Declare destructors virtual in polymorphic base classes
       1、为什么要申明虚函数
       C++程序设计中通常会存在一个基类有多个派生类型,而基类中的存在的大都是纯虚函数,需要派生类型实现。而这样的情况下,通过使用factory模式返回一个基类型的指针。在C++中明确指出,一个派生类型经过由一个基类型指针被删除,而该基类型带着一个non-virtual析构函数其结果未定义。只会造成一个局部的销毁,即基类型资源被释放,而派生类型造成memory leak,看下面的代码:
// Virtual_Const.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base{
public:
	Base(){}
	~Base(){ cout<<"~Base()"<<endl;}不带virtual时输出的结果请看图一
	//virtual ~Base(){ cout<<"~Base()"<<endl;}正确的做法带virtual时输出的结果请看图二
};
	class Derived:public Base{
	public:
		Derived()
		{
			p=(char*)malloc(sizeof(char)*10);
		}
		~Derived()
		{
			free(p);
			cout<<"~Derived()"<<endl;
		}
	private:
		char *p;
	};
	int _tmain(int argc, _TCHAR* argv[])
	{
		Base *ptr=new Derived();
		if (ptr==NULL)
		{
			cout<<"未分配成功"<<endl;
			exit(1);
		}
		delete ptr;
		return 0;
	}
图一:不带virtual时输出结果:
图二:带virtual时输出结果:
 
以上代码和输出结果可以看出,Derived类继承了Base类,并且在heap上申请了内存资源,在它的析构函数中被释放。但由于Base的析构函数是non-virtual,所以根本没有正确释放Derived类型中的ptr 指针。
2、virtual 函数实现内部机制
       
         polymorphic(带多态性质的)base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不应该声明virtual析构函数。那么是不是所有的class都加上virtual析构函数以保万一,事实不是如此。如果没有必要,请不要为析构函数加上virtual二字。原因是virtual是有代价的,为了实现virtual函数,类中间必须要增加一个pointer指向虚函数表,这样增大了类的体积。所以没有必要的话,还是不要随意声明virtual的析构函数。普遍的规则是只有当类当中有virtual的函数时,析构函数才声明为virtual。也就是说这个基类是有多态性质(polymorphic)的。
        虚函数的实现需要它所在的对象包含额外的信息,这一信息用来在运行时确定本对象需要调用哪个虚函数。通常,这一信息采取一个指针的形式,这个指针被称为“ vptr ”(“虚函数表指针”)。 vptr 指向一个包含函数指针的数组,这一数组称为“ vtbl ”(“虚函数表”),每个包含虚函数的类都有一个与之相关的 vtbl 。当一个虚函数被一个对象调用时,就用到了该对象的 vptr 所指向的 vtbl ,在 vtbl 中查找一个合适的函数指针,然后调用相应的实函数。
请看如下代码,就知道声明virtual函数是要浪费系统资源的。
 
// sev_vir.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
class class1{};
class class2
{
public:
virtual ~ class2();
};
int _tmain(int argc, _TCHAR* argv[])
{
    cout<<sizeof(class1)<<endl;
    cout<<sizeof(class2)<<endl;
    return 0;
}

3、请注意

       不过还有一个问题,那就是继承那些不带virtual函数的标准类库的类,比如string类。如果你继承了string类,那么当你使用string的指针释放你的继承类的话,依然存在上面描述的问题,你的继承类没有释放它该释放的空间。因为string没有virtual的析构函数。所以请不要去继承这些并没有virtual析构函数的类。

        还有就是STL中的容器通常继承都毫无意义,因为它们也都没有提供virtual析构函数。C++并没有提供像JAVA或者C#的final和sealed这样的约束关键字来禁止某个类型被继承。 并不是每个类型都会被用来做继承用途,也不是每个类型设计出来被用来做为多态用途,所以正确的设计相当重要。

4、纯虚函数实现

// Pure_vir.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
class Base //abstract class
{
public:
    virtual ~Base(){};//virtual, not pure
    virtual void func() const = 0;//pure virtual
    
};
void Base::func() const //pure virtual also can have function body
{
    cout <<"Base::func"<<endl;
}
class Derived : public Base
{
public:
    Derived(){}
    virtual void func() const
    {
        Base::func();
        cout <<"Derived::func"<<endl;
    }
    virtual void foo(){}
};
int _tmain(int argc, _TCHAR* argv[])
{
    Base* pb=new Derived();
    pb->func();
    pb->Base::func();
    return 0;
} 


  从生成的结果我们可以看到有纯虚函数的类是抽象类,不能生成对象,只能派生。

        如果他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。定义纯虚函数就是为了让基类不可实例化化, 因为实例化这样的抽象数据结构本身并没有意义。或者给出实现也没有意义实际上我个人认为纯虚函数的引入 ,为了安全.因为避免任何需要明确但是因为不小心而导致的未知的结果,提醒子类去做应做的实现.。

请记住

1、polymorphic(带多态性质的) base classes应该声明一个virtual 析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。

2、Classes的设计目的如果不是作为base classes使用,或不是为了具备多态性质(polymorphically),就不该声明virtual析构函数。

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值