虚析构函数的小结

来源:互联网 发布:软件开发简历范文 编辑:程序博客网 时间:2024/05/21 23:28

如果我们的类存在派生,应该在顶端类中定义虚析构函数,如果基类的析构函数不是一个虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为,会造成内存泄漏。虚函数依赖虚函数表进行工作。如果一个类中,有函数被关键词virtual进行修饰, 那么一个虚函数表就会被构建起来保存这个类中虚函数的地址。同时, 编译器会为这个类添加一个隐藏指针指向虚函数表。如果在派生类中没有重写虚函数, 那么,派生类中虚表存储的是父类虚函数的地址。每当虚函数被调用时, 虚表会决定具体去调用哪个函数。因此,C++中的动态绑定是通过虚函数表机制进行的。

#include<iostream> using namespace std; class ClxBase{ public:     ClxBase() {};     ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};    void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; };class ClxDerived : public ClxBase{ public:     ClxDerived() {};     ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }; };   int   main(){   ClxDerived *p =  new ClxDerived;   p->DoSomething();   delete p;   return 0;   }

运行结果:

Do something in class ClxDerived!

Output from the destructor of class ClxDerived!

Output from the destructor of class ClxBase!

这段代码中基类的析构函数不是虚函数,在main函数中用继承类的指针去操作继承类的成员,释放指针P的过程是:先释放继承类的资源,再释放基类资源.
#include<iostream> using namespace std; class ClxBase{ public:     ClxBase() {};     ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};    void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; };class ClxDerived : public ClxBase{ public:     ClxDerived() {};     ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; } };   int   main(){     ClxBase *p =  new ClxDerived;   p->DoSomething();   delete p;   return 0;   } 

输出结果:

Do something in class ClxBase!
Output from the destructor of class ClxBase!

这段代码中基类的析构函数同样不是虚函数,不同的是在main函数中用基类的指针去操作继承类的成员,释放指针P的过程是:只是释放了基类的资源,而没有调用继承类的析构函数.调用dosomething()函数执行的也是基类定义的函数.一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏.在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数.析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的. 
#include<iostream> using namespace std; class ClxBase{ public:     ClxBase() {};     virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};     virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; };class ClxDerived : public ClxBase{ public:     ClxDerived() {};     ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };     void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }; };  int   main(){     ClxBase *p =  new ClxDerived;   p->DoSomething();   delete p;   return 0;   }  

这段代码中基类的析构函数被定义为虚函数,在main函数中用基类的指针去操作继承类的成员,释放指针P的过程是:只是释放了继承类的资源,再调用基类的析构函数.调用dosomething()函数执行的也是继承类定义的函数.
如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数.
(http://blog.sina.com.cn/s/blog_7c773cc50100y9hz.html)

在此再说一下虚析构函数的原理:如果父类当中定义了虚析构函数,那么父类的虚函数表当中就会有一个父类的虚析构函数的入口指针,指向的是父类的虚析构函数,子类虚函数表当中也会产生一个子类的虚析构函数的入口指针,指向的是子类的虚析构函数,这个时候使用父类的指针指向子类的对象,delete接父类指针,就会通过指向的子类的对象找到子类的虚函数表指针,从而找到虚函数表,再虚函数表中找到子类的虚析构函数,从而使得子类的析构函数得以执行,子类的析构函数执行之后系统会自动执行父类的虚析构函数。这个是虚析构函数的实现原理。

原创粉丝点击