zoukankan      html  css  js  c++  java
  • 多态中的虚析构函数

    为什么析构函数要声明成virtual呢?

    因为,如果delete一个基类的指针时, 如果它指向的是一个子类的对象,那么析构函数不为虚就会导致无法调用子类析构函数,从而导致资源泄露。

    如果一个类要被使用成多态的,那么这个virtual是必须的。比如:

    #include <iostream>
    using namespace std;
    
    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()
    {
        Animal* pa = new Dog();
        pa->foo();
        delete pa;
        return 0;
    }
    

    delete pa 实际上相当于:

    pa->~Animal();
    

    释放pa所指向的内存

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

    如果基类析构函数不加virtual,运行效果如下:

    Animal ctor
    Dog ctor
    Dog::foo
    Animal dtor
    

    可另一个问题是,有时virtual是不需要的。

    如果一个类不会被继承,比如一个utility类,该类完全是静态方法;

    或者一些类尽管可能会被继承,但不会被使用成多态的,即除了析构函数外,没有其他的方法是virtual的,这时就可以把virtual属性去掉。

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

    同样,当作一个抽象时,如果你模仿Java的interface,声明了如下的虚基类:

    class AbstractBase
    {
        virtual method1() = 0;
        virtual method2() = 0;
    };
    

    那么应该给它增加一个空的virtual析构函数:

    virtual ~AbstractBase(){}
    

    如果你对COM比较熟悉,可能会注意到,COM interface中并没有这个virutal构造函数。这是因为,COM通过使用引用计数的机制来维护对象。当你使用完一个COM对象,调用Release()时,COM的内部实现检查引用技术是否为零。如果是,则调用:

    delete this;
    

    因为Release()是virtual的,所以该COM对象对应的正确的派生类被调用,delete this会调用正确的析构函数,达到了使用virtual析构函数的效果。

  • 相关阅读:
    IntelliJ IDEA Java编译打包
    java 播放mp3文件
    cool edit录音声卡
    VB调用VB脚本VBS向Http请求的三种方式
    asp.net 各种下载方式汇总
    Action拦截器接口-- IActionFilter,IExceptionFilter, IResultFilter
    Centos 常用命令
    医院信息集成平台ESB技术框架
    【划重点】医疗软件行业关键概念扫盲
    互联网医院安全架构
  • 原文地址:https://www.cnblogs.com/WindSun/p/11434408.html
Copyright © 2011-2022 走看看