指向子类的指针上的delete是否调用基类的析构函数?


165

我有一个class A使用堆内存分配为其字段之一。类A被实例化并作为指针字段存储在另一个类(class B

当我完成了类B的对象时,我会调用delete,我假设它调用了析构函数...但是这是否也调用了类A的析构函数?

编辑:

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

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

Answers:


183

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

我不知道,当你释放内存只有部分调用析构函数(例如,使用错误的指针。)
托马什Zato -恢复莫妮卡

指针只是一个数字。您甚至可以不小心++在其上使用运算符。所以我想知道指向类数据中间的指针是否仍然有效。
托马什Zato -恢复莫妮卡

2
@TomášZato:如果您在随机指针上调用delete,那么您就很困惑。这样做从来没有充分的理由。实际上,如果您要在智能指针析构函数以外的任何地方手动调用delete,那么您可能想再看看为什么不使用智能指针或其他对象管理器。

shared_array仅来自boost,是吗?
德隆兹

30

当您对由new分配的指针调用delete时,将调用指向的对象的析构函数。

A * p = new A;

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

22

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

在每个类的析构函数中,您必须删除所有其他已分配了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甚至没有析构函数)。


5

如果您有一个通常的指针(A*),那么将不会调用析构函数(A例如,也不会释放内存),除非您deleteB的析构函数中进行了明确声明。如果要自动销毁,请查看诸如的智能指针auto_ptr



4
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成员。删除它们也会调用其析构函数。


1

你有类似的东西

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

如果再调用delete b;,则a不会发生任何事情,并且内存泄漏。尝试记住delete b->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为b时,它将被自动删除。

这些方法中的任何一种都能很好地工作,而我都使用了这两种方法。


1

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


提高编译器警告级别
Phil1970年

0

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


我正在这样做,我的问题是析构函数被调用了吗?
尼克·博尔顿


0

不,它不会调用类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;
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.