- // 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;
- }


以上代码和输出结果可以看出,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析构函数。