C++将析构函数定义成virtual的原因

来源:互联网 发布:怎么在淘宝买电棒 编辑:程序博客网 时间:2024/06/11 21:52

疑问:为什么在C++的实际使用中继承子类的虚构函数有的时候需要添加virtual有时候不需要添加virutal


1. 一般来说,如果一个类要被另外一个类继承,而且用其指针指向其子类对象时,例如:
A* d = new B();(假定A是基类,B是从A继承而来的派生类)
那么其(A类)析构函数必须是虚的,否则在delete d时,B类的析构函数将不会被调用,因而会产生内存泄漏和异常; 


2. 在构造一个类的对象时,先构造其基类子对象,即调用其基类的构造函数,然后调用本类的构造函数;销毁对象时,先调用本类的析构函数,然后再调用其基类的构造函数;


举个例子:

#include "stdio.h"
#include <iostream>


class Animal
{
    char *ap;
public:


    Animal()
    {
        ap = new char;
        std::cout << "Animal ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Animal::foo" << std::endl;
    }
    virtual ~Animal()
    {
        std::cout << "Animal dtor" << std::endl;
        delete ap;
    }
};


class Dog : public Animal
{
    char *dp;
public:
    Dog()
    {
        dp = new char;
        std::cout << "Dog ctor" << std::endl;
    }
    virtual void foo()
    {
        std::cout << "Dog::foo" << std::endl;
    }
    virtual ~Dog()
    {
        delete dp;
        std::cout << "Dog dtor" << std::endl;
    }
};


int main(int argc,char* argv[])
{
    Animal *pa = new Dog();
    pa->foo();
    delete pa;
    return 0;
}

代码输出如下:


delete pa 实际上相当于:pa->~Animal();释放pa所指向的内存(或许是free(pa))。
在这里,因为~Animal()是virtual的,尽管是通过Animal类型的指针调用的,根据v-table的信息,~Dog()被正确调用到。如果把virtual属性去掉,那么被调用的是~Animal(),Dog类的构造函数被调用而析构函数未被调用,构造函数中分配的资源没有释放,从而产生了内存泄漏。析构函数缺省声明为virtual,就可以避免这一问题。


如果把代码中的红色的“virutual”(基类Animal析构函数前的virtual)删除的话,输出如下:


明显Dog的析构函数没有被调用,内存泄漏了


可另一个问题是,有时virtual是不需要的。如果一个类不会被继承,比如一个utility类,该类完全是静态方法;或者一些类尽管可能会被继承,但不会被使用成多态的,即除了析构函数外,没有其他的方法是virtual的,这时就可以把virtual属性去掉。


去掉析构函数的virtual属性后,因为该类中没有其他的virtual函数,所以编译时不会生成v-table,这样就节省了编译时间,并减少了最终生成的程序的大小。更重要的是,遵从这一规则,给该类的维护者一个信息,即该类不应被当作多态类使用。

阅读全文
0 0