Подтвердить что ты не робот

Почему деструктор по умолчанию для С++ не уничтожает мои объекты?

В спецификации С++ деструктор по умолчанию удаляет все нестатические элементы. Тем не менее, я не могу этого добиться.

У меня есть это:

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
        n = new N;
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N* n;
};

Затем это должно напечатать данное сообщение, но это не так:

M* m = new M();
delete m; //this should invoke the default destructor
4b9b3361

Ответ 1

Что заставляет вас думать, что объект n указывает на необходимость удаления по умолчанию? Деструктор по умолчанию уничтожает указатель, а не то, что он указывает.

Изменить: я посмотрю, смогу ли я сделать это немного понятным.

Если у вас был локальный указатель, и он вышел из области видимости, вы ожидаете, что объект, который он указывает на уничтожение?

{
    Thing* t = new Thing;

    // do some stuff here

    // no "delete t;"
}

Указатель t очищается, но Thing указывает на отсутствие. Это утечка. По сути, то же самое происходит в вашем классе.

Ответ 2

Представьте себе что-то вроде этого:

class M {
public:
    M() { }
//  ~M() {        // If this happens by default
//      delete n; // then this will delete an arbitrary pointer!
//  }
private:
    N* n;
};

Вы сами по себе с указателями на С++. Никто не будет автоматически удалять их для вас.

Деструктор по умолчанию действительно уничтожит все объекты-члены. Но объект-член в этом случае является самим указателем, а не тем, на что он указывает. Это могло бы смутить вас.

Однако, если вместо простого встроенного указателя вы будете использовать умный указатель, уничтожение такого "указателя" (который фактически является классом ) может вызвать разрушение объекта, на который указывает.

Ответ 3

Деструктор по умолчанию уничтожает указатель. Если вы хотите удалить N с M деструктором по умолчанию, используйте умный указатель. Изменить N * n; на auto_ptr<N> n; и N будут уничтожены.

Изменить: Как указано в комментариях, auto_ptr<> не лучший умный указатель для всех видов использования, но он выглядит так, как это требуется здесь. Он конкретно представляет собой собственность: N в M существует на время M и больше не существует. Копирование или назначение auto_ptr<> представляет собой изменение в собственности, которое обычно не то, что вы хотите. Если вы хотите передать указатель из M, вы должны передать N * полученный из n.get().

Более общее решение будет boost::shared_ptr<>, которое будет в стандарте С++ 0x. Это можно использовать практически везде, где будет использоваться необработанный указатель. Это не самая эффективная конструкция и имеет проблемы с круговыми ссылками, но обычно это безопасная конструкция.

Другое редактирование. Чтобы ответить на вопрос в другом комментарии, стандартное поведение деструктора по умолчанию - уничтожить все члены данных и базовые классы. Однако удаление необработанного указателя просто удаляет указатель, а не то, на что указывает. В конце концов, реализация не может знать, является ли это единственным указателем, или важным, или что-то в этом роде. Идея умных указателей заключается в том, что удаление умного указателя приведет, по меньшей мере, к удалению того, на что оно указывает, что обычно является желаемым поведением.

Ответ 4

Есть ли причина, по которой вы используете указатель, когда объект, на который указывает объект, кажется, принадлежит содержащемуся объекту? Просто сохраните объект по значению:

class M
{
    N n;

public:

    M() : n()
    {
    }
};

Ответ 5

Неверно утверждать, что деструктор удаляет элементы. Он вызывает деструктор каждого элемента (и базового класса), который для встроенных типов (например, указателей) означает ничего не делать.

Соответствие new s с delete s - ваша ответственность (вручную или с помощью умных указателей).

Ответ 6

Ваш аргумент может показаться звуковым, но это не то, как все работает для указателей.

n фактически разрушается, но это означает, что деструктор N* вызывается, который не уничтожает любой объект, на который указывает n. Подумайте о деструкторе N*, как будто это деструктор int. Он удаляет его значение, то же самое происходит с указателем, он удаляет адрес, на который он указывает, но ему не нужно удалять любой объект по адресу, который вы только что удалили.

Ответ 7

Я думаю, вы можете быть смущены об уровнях косвенности здесь. Когда экземпляр уничтожается, каждый элемент данных действительно уничтожается вместе с ним. В вашем случае, когда M уничтожается и вызывается M::~M(), его переменная n действительно уничтожается. Проблема состоит в том, что n является N *, поэтому, пока указатель уничтожен, предмет, на который он указывает, не является.

delete не работает так. Рассмотрим ваше простое утверждение:

delete n;

Вышеупомянутое утверждение уничтожает вещь, на которую указывает n, что является объектом типа n. Он не уничтожает сам n, который является указателем N *.

Существует очень веская причина, что M::~M() не вызывает автоматически delete n;, который является следующим: объект n, о котором упоминалось, может быть разделен между несколькими объектами M, и если один M был уничтожен, остальные потеряли бы n, на которые они указывали, оставляя ужасные висящие указатели повсюду. С++ не пытается интерпретировать то, что вы хотели сделать с вашими указателями, он просто делает то, что вы ему сказали.

Короче говоря, M действительно уничтожает всех своих членов, когда он уничтожается, просто потому, что это уничтожение не делает то, что вы думаете, что он должен делать. Если вам нужен тип указателя, который принимает собственность на объект и уничтожает его при уничтожении указателя, просмотрите std::auto_ptr.

Ответ 8

Деструктор по умолчанию выглядит так:

~M()
{
}

Деструктор по умолчанию не вставляет код, чтобы делать что-либо с заостренными вещами. Что делать, если вы указали n на переменную стека? Автоматическая вставка удаления n приведет к сбою.

Деструктор по умолчанию вызывает деструктор для каждого члена класса (член. ~ T()). Для указателя, что no-op (ничего не делает), так же, как myint. ~ Int() ничего не делает, но для классов-членов с определенными деструкторами вызывается деструктор.

Вот еще один пример:

struct MyClass {
public:
    MyClass() { .. } // doesn't matter what this does

    int x;
    int* p;
    std::string s;
    std::vector<int> v;
};

Деструктор по умолчанию в действительности делает это:

MyClass::~MyClass()
{
    // Call destructor on member variables in reverse order
    v.~std::vector<int>(); // frees memory
    s.~std::string();      // frees memory
    p.~int*();             // does nothing, no custom destructor
    x.~int();              // does nothing, no custom destructor
}

Конечно, если вы определяете деструктор, код в вашем деструкторе выполняется до того, как будут уничтожены переменные-члены (очевидно, в противном случае они не будут действительны!).

Ответ 9

Попробуйте избежать использования указателей. Они являются последними элементами курорта.

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
       // n = new N; no need, default constructor by default
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N n; // No pointer here
};

Затем используйте его таким образом

main(int, char**)
{
    M m;
}

Отобразится объект Destroying типа N

Ответ 10

Я думаю, вы могли бы воспользоваться очень простым примером:

int main(int argc, char* argv[])
{
  N* n = new N();
} // n is destructed here

Это тоже ничего не печатает.

Почему? Поскольку pointer (n) уничтожается, а не объект, указывающий на *n.

Конечно, вы не захотите уничтожить объект, на который указывает:

int main(int argc, char* argv[])
{
  N myObject;
  {
    N* n = &myObject;
  } // n is destructed here, myObject is not

  myObject.foo();
} // myObject is destructed here

Вы должны помнить, что в отличие от языков типа C# или Java существует два способа создания объектов в С++: непосредственно N myObject (в стеке) или через new, как в new N(), и в этом случае объект помещается в кучу, и вы будете отвечать за его освобождение позднее.

Таким образом, ваш деструктор уничтожает указатель, но не объект, на который указывает. Выделите объект без нового (и не используя указатель) или используйте Smart Pointer, если вы хотите, чтобы он был автоматическим.

Ответ 11

M destructor должен иметь "удалить n".

Ответ 12

Поскольку вы используете new для создания экземпляра, он по умолчанию не будет удалять.

Ответ 13

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
        n = new N;
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N* n;
};

и теперь ожидание:

M* m = new M();
delete m; //this should invoke the default destructor

Это произойдет только в том случае, если класс M получен из N:

class M: Class N {
...

Только в этой ситуации

M* m = new M()

вызовет конструктор N, а затем конструктор M, где as

delete m;

автоматически вызовет деструктор M сначала, а затем N