Соглашения по деструкторам C ++

Я вижу много кода, подобного примеру ниже в C ++, который обычно рекомендуется как соглашение:

class Foo
{
public:

Foo()
{
bar = new int[100];
size = 100;
}

// ... copy/assignment stuff ...

~Foo()
{
if (bar) // <--- needed?
{
delete[] bar;
bar = nullptr; // <--- needed?
}

size = 0; // <--- needed?
}

private:

int* bar;
int size;
};

Для меня три утверждения if (bar) { ... }, bar = nullptr;, а также size = 0; избыточны по двум причинам:

  1. delete nullptr; совершенно безопасен, и ничего не делает.
  2. Если объект уничтожен, а память освобождена, мне не стоит беспокоиться о безопасности bar в nullptr а также size до 0.

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

3

Решение

Вы правы, они не нужны, и некоторые компиляторы все равно их оптимизируют.

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

6

Другие решения

Где это usually recommended as a convention?

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

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

4

ты должен использовать delete[] (Массив)

Это правда, что if бесполезно. Воздействием с точки зрения производительности обычно можно пренебречь. Когда вы отлаживаете, вы иногда счастливы, что все в безопасном состоянии (например, не смотрите на свободную память и почесываете голову). Когда он спрятан глубоко внутри дерева, это помогает (по крайней мере, повторная инициализация размера и полосы).

Кстати, лучший способ написать это было бы (RAII: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization )

Foo():
bar (new int[100]),
size(100)
{
}

РЕДАКТИРОВАТЬ :

  • старые привычки тяжело умирают, для этого вы должны использовать scoped_ptr и быть счастливым с этим (или даже лучше, vector) и убить старые добрые тупые указатели.
  • пожилые парни написали слишком много C или поменяли слишком много языков: лучше, чем потом сожалеть, когда это дешево
0

Как уже упоминалось во многих других ответах, if(bar) довольно глупо

Установка освобожденных указателей на nullptr и сброс size = 0 имеет свои применения, хотя. Некоторые из этих избыточностей хороши в ООП, когда у вас есть виртуальные конструкторы, виртуальные деструкторы и иерархии классов. Во время деструктора, что происходит, если дочерний класс освобождает указатель, но не устанавливает его в nullptr, а потом базовый класс тоже пытается его освободить? В аналогичном случае, что происходит, если базовый класс предполагает что указатель действителен, если size > 0?

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

0