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

疑问:为什么在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,这样就节省了编译时间,并减少了最终生成的程序的大小。更重要的是,遵从这一规则,给该类的维护者一个信息,即该类不应被当作多态类使用。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值