Я вижу много кода, подобного примеру ниже в 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;
избыточны по двум причинам:
delete nullptr;
совершенно безопасен, и ничего не делает.bar
в nullptr
а также size
до 0.Являются ли эти обоснования правильными? Действительно ли эти утверждения излишни? Если так, почему люди продолжают использовать и предлагать их? Я хотел бы увидеть некоторые потенциальные проблемы, которые могут быть решены путем соблюдения этого соглашения.
Вы правы, они не нужны, и некоторые компиляторы все равно их оптимизируют.
тем не мение — люди обычно делают это, чтобы помочь выявить проблемы. Например, скажем, вы не устанавливаете указатель на ноль. Объект разрушен, но тогда вы неверно попытка получить доступ к (что когда-то было) указателю. Поскольку среда выполнения, вероятно, не очистит его, вы все равно увидите там что-то действительное. Это полезно только при отладке, и это все еще неопределенное поведение, но иногда оно окупается.
Где это usually recommended as a convention
?
Обычно рекомендуется НЕ управлять распределением памяти вручную. Если вы использовали std::vector
(или умный указатель, если вы действительно хотите), чтобы реализовать это, все проблемы полностью исчезают, и вам вообще не нужно писать деструктор.
Однако если вы действительно настаивая на этом (и вы написали копирование конструктора / назначения копирования, которые вы не показали нам, для корректности), я обнаружил, что дополнительная работа в деструкторе фактически скрывает то, что происходит на самом деле, и обеспечивает небольшую ценность за счет кода запутывания.
ты должен использовать delete[]
(Массив)
Это правда, что if
бесполезно. Воздействием с точки зрения производительности обычно можно пренебречь. Когда вы отлаживаете, вы иногда счастливы, что все в безопасном состоянии (например, не смотрите на свободную память и почесываете голову). Когда он спрятан глубоко внутри дерева, это помогает (по крайней мере, повторная инициализация размера и полосы).
Кстати, лучший способ написать это было бы (RAII: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization )
Foo():
bar (new int[100]),
size(100)
{
}
РЕДАКТИРОВАТЬ :
scoped_ptr
и быть счастливым с этим (или даже лучше, vector
) и убить старые добрые тупые указатели.Как уже упоминалось во многих других ответах, if(bar)
довольно глупо
Установка освобожденных указателей на nullptr
и сброс size = 0
имеет свои применения, хотя. Некоторые из этих избыточностей хороши в ООП, когда у вас есть виртуальные конструкторы, виртуальные деструкторы и иерархии классов. Во время деструктора, что происходит, если дочерний класс освобождает указатель, но не устанавливает его в nullptr
, а потом базовый класс тоже пытается его освободить? В аналогичном случае, что происходит, если базовый класс предполагает что указатель действителен, если size > 0
?
Есть мелкие детали бухгалтерии, подобные этим, которые могут создавать тонкие ошибки, которые чрезвычайно сложно отлаживать. Также может быть педантичным в некоторых из этих случаев и позволить компилятору убрать то, что не нужно.