【问题标题】:Does delete on a pointer to a subclass call the base class destructor?删除指向子类的指针会调用基类析构函数吗?
【发布时间】:2010-10-15 05:27:18
【问题描述】:

我有一个class A,它为其一个字段使用堆内存分配。类 A 被实例化并存储为另一个类中的指针字段 (class B.

当我处理完 B 类的对象后,我调用 delete,我假设它调用了析构函数……但这是否也调用了 A 类的析构函数?

编辑:

从答案中,我认为(如果不正确,请编辑):

  1. B 实例的delete 调用 B::~B();
  2. 调用A::~A();
  3. A::~A 应该明确地delete A 对象的所有堆分配成员变量;
  4. 最后,存储 B 类实例的内存块返回到堆 - 当使用 new 时,它首先在堆上分配一块内存,然后调用构造函数对其进行初始化,现在之后已调用所有析构函数以完成对象,将对象所在的块返回到堆中。

【问题讨论】:

    标签: c++ memory-management destructor delete-operator base-class


    【解决方案1】:

    A 的析构函数将在其生命周期结束时运行。如果你想释放它的内存并运行析构函数,如果它是在堆上分配的,你必须删除它。如果它是在堆栈上分配的,这会自动发生(即,当它超出范围时;请参阅 RAII)。如果它是一个类的成员(不是指针,而是一个完整的成员),那么当包含的对象被销毁时就会发生这种情况。

    class A
    {
        char *someHeapMemory;
    public:
        A() : someHeapMemory(new char[1000]) {}
        ~A() { delete[] someHeapMemory; }
    };
    
    class B
    {
        A* APtr;
    public:
        B() : APtr(new A()) {}
        ~B() { delete APtr; }
    };
    
    class C
    {
        A Amember;
    public:
        C() : Amember() {}
        ~C() {} // A is freed / destructed automatically.
    };
    
    int main()
    {
        B* BPtr = new B();
        delete BPtr; // Calls ~B() which calls ~A() 
        C *CPtr = new C();
        delete CPtr;
        B b;
        C c;
    } // b and c are freed/destructed automatically
    

    在上面的例子中,每个 delete 和 delete[] 都是必需的。在我没有使用的地方不需要删除(或者确实可以使用)。

    auto_ptrunique_ptrshared_ptr 等...非常适合简化生命周期管理:

    class A
    {
        shared_array<char> someHeapMemory;
    public:
        A() : someHeapMemory(new char[1000]) {}
        ~A() { } // someHeapMemory is delete[]d automatically
    };
    
    class B
    {
        shared_ptr<A> APtr;
    public:
        B() : APtr(new A()) {}
        ~B() {  } // APtr is deleted automatically
    };
    
    int main()
    {
        shared_ptr<B> BPtr = new B();
    } // BPtr is deleted automatically
    

    【讨论】:

    • 我想知道当你只释放部分内存(例如使用错误的指针)时是否调用了析构函数
    • 指针只是一个数字。您甚至可以不小心在其上使用++ 运算符。所以我想知道指向类数据中间的指针是否仍然有效。
    • @TomášZato:如果你在随机指针上调用 delete,那么你就完蛋了。这样做从来没有充分的理由。事实上,如果您在智能指针析构函数之外的任何地方手动调用 delete,那么您可能想再看看为什么不使用智能指针或其他对象管理器。
    • shared_array 仅来自 boost,是吗?
    【解决方案2】:

    当你对new分配的指针调用delete时,所指向对象的析构函数会被调用。

    A * p = new A;
    
    delete p;    // A:~A() called for you on obkect pointed to by p
    

    【讨论】:

      【解决方案3】:

      它被命名为“析构函数”,而不是“解构函数”。

      在每个类的析构函数中,你必须删除所有其他已经被new分配的成员变量。

      编辑:澄清:

      说你有

      struct A {}
      
      class B {
          A *a;
      public:
          B () : a (new A) {}
          ~B() { delete a; }
      };
      
      class C {
          A *a;
      public:
          C () : a (new A) {}        
      };
      
      int main () {
          delete new B;
          delete new C;
      }
      

      分配B的一个实例然后删除是干净的,因为B在内部分配的东西也会在析构函数中被删除。

      但是类 C 的实例会泄漏内存,因为它分配了一个它不会释放的 A 实例(在这种情况下 C 甚至没有析构函数)。

      【讨论】:

        【解决方案4】:

        如果你有一个常用的指针(A*),那么析构函数将不会被调用(并且A 实例的内存也不会被释放),除非你在B 的析构函数中明确地执行delete。如果您想自动销毁,请查看 auto_ptr 之类的智能指针。

        【讨论】:

          【解决方案5】:

          你应该在 B 的析构函数中自己删除 A。

          【讨论】:

            【解决方案6】:
            class B
            {
            public:
                B()
                {
                   p = new int[1024];  
                }
                virtual ~B()
                {
                    cout<<"B destructor"<<endl;
                    //p will not be deleted EVER unless you do it manually.
                }
                int *p;
            };
            
            
            class D : public B
            {
            public:
                virtual ~D()
                {
                    cout<<"D destructor"<<endl;
                }
            };
            

            当你这样做时:

            B *pD = new D();
            delete pD;
            

            只有当你的基类有 virtual 关键字时才会调用析构函数。

            然后,如果您没有虚拟析构函数,则只会调用 ~B()。但是由于你有一个虚拟析构函数,所以首先调用 ~D(),然后调用 ~B()。

            在堆上分配的 B 或 D 的任何成员都不会被释放,除非您明确删除它们。删除它们也会调用它们的析构函数。

            【讨论】:

              【解决方案7】:

              你有类似的东西

              class B
              {
                 A * a;
              }
              B * b = new B;
              b->a = new A;
              

              如果您随后调用 delete b;,则 a 不会发生任何事情,并且您有内存泄漏。尝试记住 delete b-&gt;a; 不是一个好的解决方案,但还有其他几个。

              B::~B() {delete a;}
              

              这是 B 的析构函数,将删除 a。 (如果 a 为 0,则删除不执行任何操作。如果 a 不为 0 但未从 new 指向内存,则会导致堆损坏。)

              auto_ptr<A> a;
              ...
              b->a.reset(new A);
              

              这样你就没有 a 作为指针,而是一个 auto_ptr (shared_ptr 也可以,或者其他智能指针),当 b 是时它会自动删除。

              这两种方法都很好用,我都用过。

              【讨论】:

                【解决方案8】:

                我想知道为什么我的类的析构函数没有被调用。原因是我忘记包含该类的定义(#include "class.h")。我只有一个像“A级”这样的声明;编译器对此很满意,让我称之为“删除”。

                【讨论】:

                • 提高编译器警告级别
                【解决方案9】:

                没有。指针将被删除。您应该在 B 的析构函数中显式调用 A 上的删除。

                【讨论】:

                • 我正在这样做,我的问题是析构函数被调用了吗?
                【解决方案10】:

                只有当为该对象调用 delete 时,才会调用类 A 对象的析构函数。确保在 B 类的析构函数中删除该指针。

                有关在对象上调用 delete 时会发生什么的更多信息,请参阅: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9

                【讨论】:

                  【解决方案11】:

                  不,它不会为 A 类调用析构函数,你应该显式调用它(就像 PoweRoy 所说的那样),删除行 'delete ptr;'例如比较...

                    #include <iostream>
                  
                    class A
                    {
                       public:
                          A(){};
                          ~A();
                    };
                  
                    A::~A()
                    {
                       std::cout << "Destructor of A" << std::endl;
                    }
                  
                    class B
                    {
                       public:
                          B(){ptr = new A();};
                          ~B();
                       private:
                          A* ptr;
                    };
                  
                    B::~B()
                    {
                       delete ptr;
                       std::cout << "Destructor of B" << std::endl;
                    }
                  
                    int main()
                    {
                       B* b = new B();
                       delete b;
                       return 0;
                    }
                  

                  【讨论】:

                    猜你喜欢
                    • 2020-12-27
                    • 2013-03-27
                    • 2018-09-12
                    • 1970-01-01
                    • 2020-07-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多