• 给我个理由要用虚析构函数


    什么时候要用虚析构函数?
    通过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。
    一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,从而千万内存泄漏。
    原因:
    在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数。
    那么,析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。
    注意:
    如果不需要基类对派生类及对象进行操作,则不能定义虚函数(包括虚析构函数),因为这样会增加内存开销。
    语法如下:
    class Base
    ...{ public:
           Base( ) ...{   };
       virtual     ~Base( ) ...{   };   //注意:基类的析构函数被定义为虚的
    };
    class Derived
    ...{
       public:
           Derived( ) ...{   };
         ~Derived( ) ...{   };
    };
    void main( )
    ...{
       Base *p;
       p = new Derived;
       delete p;
    }
    注:似乎这样的使用经常在new和delete行为时。
    =================================================
    我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:   
         有下面的两个类:
    class ClxBase
    {
    public:
         ClxBase() {};
         virtual ~ClxBase() {};
         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; };
    };
         代码
    ClxBase *pTest = new ClxDerived;
    pTest->DoSomething();
    delete pTest;
         的输出结果是:
    Do something in class ClxDerived!
    Output from the destructor of class ClxDerived!
         这个很简单,非常好理解。
         但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
    Do something in class ClxDerived!
    也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
         所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
         当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
    写成虚的是为了在实现多态的时候不造成内存泄露, 比如:
    class a
    {
    int aa;
    public:
    virtual ~a(){};
    };
    class b : public a
    {
    int bb;
    };
    如果你这样:
    a *pa = new b; // upcast
    然后这样:
    delete pa;
    这句delete, 如果你基类的析构函数不是虚的的话, 就会造成内存泄露, 具体表现为派生类的内存被释放了而基类没有.(疑问:似乎应该是基类内存释放而派生类内存没有释放)
    //////////////////////////参考资料的地方, Efftive C++
    在继承中使用多态来创建动态对象时, 比如上面的:a *pa = new b;
    由于pa是个基类的指针, 只能识别属于基类的部分, 所以如果没有虚析构函数的话, 那么子类中特有的部分就不会被释放, 造成"经典"的释放一半, 泄露一半的内存泄露.
    这和object slicing的原理是一样的, 至于object slicing:
    为什么继承一个没有虚析构函数的类是危险的?
      一个没有虚析构函数的类意味着不能做为一个基类。如std::string, 
    std::complex, 和 std::vector 都是这样的。为什么继承一个没有虚析构函数的
    类是危险的?当你公有继承创建一个从基类继承的相关类时,指向新类对象中的指
    针和引用实际上都指向了起源的对象。因为析构函数不是虚函数,所以当你delete
    一个这样的类时,C++就不会调用析构函数链。
    class A
    {
     public:
     ~A() // 不是虚函数
     {
     // ...
     }
    }; 
    class B: public A //错; A没有虚析构函数
    {
     public:
     ~B()
     {
     // ...
     }
    };
    int main()
    {
     A * p = new B; //看上去是对的
     delete p; //错,B的析构函没有被调用
    }

    对析构函数来讲,如果没有虚析构函数,那么基类指针在销毁对象时,所能调用的也只是本类的析构函数,继承类的析构函数则没办法调用了。这跟前面讲的封装规则是一致的。如果继承类中的析构函数有释放内存等处理,而基类无法调用,就很恐怖了 

  • 相关阅读:
    C#用递归求阶乘 n!
    视图与表的区别和联系(小结)
    ref 和 out 的异同
    什么是重载?(最文艺的理解)
    [windows c]关于winsock2.h的recv函数的记录
    [windows c]关于指针函数和参数可变函数同时应用情况的疑问
    [windows c]CreateProcess
    c++ boost asio库初学习
    c#中ObservableCollection<T>排序方法
    安装opensuse时遇到的一些问题
  • 原文地址:https://www.cnblogs.com/lvpblog/p/2476254.html
Copyright © 2020-2023  润新知