effective C++ 读书笔记 条款07 为多态基类声明virtual析构函数

条款07: 为多态基类声明virtual析构函数

1:

C++明确指出:

当子类对象经由一个父类指针被删除,而该base class带着一个non-virtual析构函数,

其结果未被定义--实际执行的 时候通常发生的现象是对象的derived成分没有被销毁。

造成“局部销毁”对象, 形成资源泄露,败坏之数据结构。

这里以我写工厂模式的代码做例子,因为我写工厂模式的代码时候没有考虑到这个问题:

#include <iostream>
#include <stdio.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
class Animal //抽象产品类 
{
	public:
		Animal()
		{
		}
		virtual ~Animal()
		{
			cout<<"调用Animal父类的析构函数"<<endl;
		}
		
	public:
		virtual void act() = 0;
}; 

class cat : public Animal//具体产品类 
{
	public:
		cat()
		{
		}
		~cat()
		{
			cout<<"调用cat子类的析构函数"<<endl;
		}
		
		
		void act()
		{
			printf("猫在叫 喵喵!!\n");
		}
};

class dog : public Animal//具体产品类 
{
	public:
		dog()
		{
		}
		~dog()
		{
		}
		
		void act()
		{
			printf("狗在叫 汪汪!!\n");
		}
		
};

class men: public Animal//具体产品类 
{
	public:
		men()
		{
		}
		~men()
		{
			
		}
		
		void act()
		{
			printf("人在叫 啊啊!!\n");
		} 
};

 
 class AnimalFactory //工厂类 
 {
 	public:
	 	 AnimalFactory()
	 	 {
	 	 }
	 	 virtual ~AnimalFactory()
	 	 {
			 cout<<"执行父类的析构函数"<<endl;
	 	 }
	 	 
		 virtual  Animal*  createAnimal()=0;
			 
 }; 
 
 class catFactory: public AnimalFactory//工厂子类 
 {
 	public:
 		catFactory()
 		{
 		}
 		~catFactory()
 		{
			cout<<"执行子类的析构函数"<<endl;
 		}
 		
 		Animal* createAnimal()
 		{
 			Animal * animal = new cat();
 			
 			return animal;
 		}
 		
 		
 };
 class menFactory: public AnimalFactory//工厂子类 
 {
 	public:
 		menFactory()
 		{
 		}
 		~menFactory()
 		{
		} 
		
		Animal* createAnimal()
		{
			Animal*  animal = new men();
			return animal;
		}
 };
 
 class dogFactory: public AnimalFactory//工厂子类 
 {
 	public:
 		dogFactory()
 		{
 		}
 		~dogFactory()
 		{
		} 
		
		Animal* createAnimal()
		{
			Animal*  animal = new dog();
			return   animal;
		}
 };
 
int main(int argc, char** argv) {
	
	AnimalFactory* animalfactory1 = new catFactory();
	Animal* cat = animalfactory1->createAnimal();
	
	cat->act();
	
	delete cat;	
	//cat = NULL;
	delete animalfactory1;
	//animalfactory1 = NULL;
	/*
	这里的两个delete,如果animal类 和 AnimalFactory类析构函数不是虚函数,
	那么现在父类指针指向一个子类对象,现在父类指针被删除,而父类析构函数
	不是虚函数,这将引发灾难:C++明确指出:当子类对象经由一个父类指针被删除
	,而该base class带着一个non-virtual析构函数,其结果未被定义--实际执行的
	时候通常发生的现象是对象的derived成分没有被销毁。造成“局部销毁”对象,
	形成资源泄露,败坏之数据结构。
	
	*/

	printf("工厂模式 by 西门吹雪");
	
	return 0;
}



/*
猫在叫 喵喵!!
调用cat子类的析构函数
调用Animal父类的析构函数
执行子类的析构函数
执行父类的析构函数
工厂模式 by 西门吹雪Press any key to continue

任何class只要带有virtial函数都几乎确定应该有个virtual析构函数。
当一个class不当做base class,令其析构函数为virtual往往是个馊主意,因为会增加对象大小(虚函数指针)。

*/

任何class只要带有virtial函数都几乎确定应该有个virtual析构函数。

当一个class不当做base class,令其析构函数为virtual往往是个馊主意,因为会增加对象大小(虚函数指针)。

2:

当一个类完全不带virtual函数,但是有的时候程序员会把它当做base class,这个时候就会出错

看代码:

#include <iostream>
#include <string>
using namespace std;

//string类完全不带virtual函数,但是有的时候程序员会把它当做base class,这个时候就会出错

class SpecialString:public string
{
public:
	SpecialString(const string& ss)
	{

	}
};



int main()
{

	SpecialString* pss = new SpecialString("Hello");
	string* ps;

	ps = pss;
	delete ps; //现实中的*ps中的SpecialString资源会被泄露,因为SpecialString析构函数未被调用。
	return 0;
}

/*
相同的分析适用于任何不带virtual析构函数的class,包括所有STL容器如vector、list、set等,如果你曾经企图继承
一个标准容器或任何其他带有“non-virtual析构函数”的class,拒绝诱惑吧。
*/

上面说的并不是说所有的没有virtual析构函数的类都不能作为基类给其他类继承,有些类如果不用做多态,那么也可以被继承。

比如:

<span style="color:#000000;">class Uncopyable
{
protected: //允许子类对象构造和析构
	Uncopyable(){}
	~Uncopyable(){}

private://但是阻止copying
	Uncopyable(const Uncopyable&);
	Uncopyable& operator = (const Uncopyable&);

};

//为了阻止测试对象被拷贝,我们唯一需要做的就是继承Uncopyable;

class Test : public Uncopyable
{

};</span>

上面的Uncopyable类不是用来实现多态,是为了实现防止copying,这个类可以被继承,但是析构函数不是虚函数,总之一句话,如果一个类用来实现多态功能,那么必须

有虚析构函数。

3:

有时候你希望拥有一个抽象class,抽象class必须有一个 pure class,但是现在你的类里面没有一个virtual函数,这个时候
应该怎么办?由于抽象class 总是企图被当做一个base class来用,而且由于base class 应该有个virtual析构函数,并且由于
pure virtual函数会导致抽象class,因此解法很简单:把析构函数声明为 pure virtual函数;

看代码:

#include <iostream>
using namespace std;

/*
有时候你希望拥有一个抽象class,抽象class必须有一个 pure class,但是现在你的类里面没有一个virtual函数,这个时候
应该怎么办?由于抽象class 总是企图被当做一个base class来用,而且由于base class 应该有个virtual析构函数,并且由于
pure virtual函数会导致抽象class,因此解法很简单:把析构函数声明为 pure virtual函数;

*/

class AWOV
{
public:
	virtual ~AWOV() = 0; //声明pure virtual 析构函数
};

//要给这个pure virtual函数提供一份定义,因为子类会调用~AWOV函数,如果不提供,链接器会发生抱怨。
AWOV::~AWOV()
{
	cout<<"我是纯虚函数"<<endl;
}

class Test:public AWOV
{

};
int main()
{
	Test test;
	return 0;
}

/*
给base class 一个Virtual析构函数,这个规则只适用于带多态性质的base class身上。

  并非所有的base class的设计目的都是为了多态用途,例如标准的string和STL容器都不被作为base class使用,
  更别提多态了。
  有些类设计目的是作为base class,但是不是用作多态,不需要虚析构函数。

*/

总结:

当我们的类当中只要有一个virtual函数,那么我们的析构函数就设置为virtual函数;

当我们的类当中没有一个virtual函数,那么我们的析构函数不要设置为virtual函数,因为这样会增加类对象的大小(虚函数指针);

当我们的类当中没有一个虚构函数,但是我们的这个类又要当做抽象类,那么把析构函数设置为纯虚函数,这个类成为抽象类,但是注意这个纯虚析构函数要有定义体;

如果一个类设计的目的不是作为base class 例如string类,就不应该声明virtual析构函数

如果一个类的设计目的不是为了具备多态的性质,也不应该声明virtual析构函数。

赞 (0) 评论 分享 ()