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

Я создал небольшую библиотечную функциональность для решения этих проблем:

Вместо:

public T DifficultCalculation(T a, T b)
{
    T result = a * b + a; // <== WILL NOT COMPILE!
    return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.

Вы могли бы написать:

public T DifficultCalculation(Number a, Number b)
{
    Number result = a * b + a;
    return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.

Вы можете найти исходный код здесь: https://codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number

28
задан Oszkar 8 March 2010 в 06:56
поделиться

12 ответов

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

Edit: I'll try if I can make this a little more clear.

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

{
    Thing* t = new Thing;

    // do some stuff here

    // no "delete t;"
}

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

46
ответ дан 28 November 2019 в 02:24
поделиться

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

1
ответ дан fastcodejava 20 November 2019 в 02:03
поделиться

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

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

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

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

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

16
ответ дан 28 November 2019 в 02:24
поделиться

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

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

4
ответ дан 28 November 2019 в 02:24
поделиться

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

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

delete n;

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

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

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

2
ответ дан 28 November 2019 в 02:24
поделиться

Я думаю, вам поможет очень простой пример:

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

Это тоже ничего не выведет.

Почему? Потому что уничтожается указатель (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, в C++ существует 2 способа создания объектов: непосредственно N myObject (на стеке) или через new, как в new N(), в этом случае объект помещается на кучу, и ВЫ несете ответственность за его освобождение в более позднее время.

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

1
ответ дан 28 November 2019 в 02:24
поделиться

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

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 object of type N

1
ответ дан 28 November 2019 в 02:24
поделиться

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

class M
{
    N n;

public:

    M() : n()
    {
    }
};
6
ответ дан 28 November 2019 в 02:24
поделиться

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

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

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

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

8
ответ дан 28 November 2019 в 02:24
поделиться

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

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

3
ответ дан 28 November 2019 в 02:24
поделиться

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

~M()
{
}

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

Деструктор по умолчанию вызывает деструктор для каждого члена класса ( member. ~ 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
}

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

2
ответ дан 28 November 2019 в 02:24
поделиться

деструктор M должен иметь 'delete n'.

1
ответ дан 28 November 2019 в 02:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: