Как мне * не * удалить участника в деструкторе?

Я бы хотел, чтобы деструктор моего класса удалил весь объект, кроме одного из членов, который был удален в другом месте. Прежде всего, это совершенно неразумно? Если это не так, как мне это сделать? Я думал, что создание деструктора с пустым телом предотвратит удаление всех членов (потому что деструктор ничего не сделает), но это не так.

12 ответов

Решение

Краткий ответ: нет.

Более длинный ответ: если "член" на самом деле является указателем на какое-то другое распределение, вы можете не удалять другое распределение.

Но обычно, если вы разместили другой блок в конструкторе, вы хотите удалить его в деструкторе. Все остальное потребует тщательной обработки "владения" данным блоком. Это будет очень похоже на управление памятью в простой c. Возможно, но чревато опасностью.

Удачи.

Зависит от того, что вы подразумеваете под "удаленным". Если они не указаны в интеллектуальном указателе и явно не удалены, они не удаляются. Члены, которые являются только частью класса:

class Bar {
//...
private: 
  Foo foo;
};

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

Если вы ищете общий ресурс "владение" между двумя местоположениями, вам нужен динамически распределенный shared_ptr:

#include <memory>
class Bar {
// ...
private:
  std::tr1::shared_ptr<Foo> foo;
};

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

Если вы хотите вместо этого удалить его в другом месте, сделайте его указателем или ссылкой.

class House
{
  Door door; //contained by value, will be destroyed when the House is
}

class House
{
  Door& door; //contained by reference, will not be destroyed when the House is
}

Код в деструкторе предназначен только для удаления членов, которые выделяются динамически. Уничтожение членов не является обязательным, вы можете только контролировать освобождение того, что вы явно выделяли ранее (с оператором new).

То, что вы хотите сделать, можно получить с помощью shared_ptr, в котором и ваш класс, и внешний код совместно используют указатель на один и тот же внешний объект. Таким образом, только когда все указатели на этот объект выйдут из области видимости, он будет удален. Но будьте осторожны, чтобы не делать циклические ссылки, shared_ptr не имеет мудрости "сборщик мусора".

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

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

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

Что касается вопроса о том, является ли это необоснованным, я думаю, что в общем-то это не так, но вы должны уточнить (обычно в документации, поскольку в C++ нет языковой поддержки этой концепции), каков объект, которому принадлежит данный член,

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

Это возможно, но в основном, как сказал @dmckee, это проблема собственности. Если это так, вы можете пойти на пересчет. т.е.

class A
{

RefObj* obj;
A()
{

obj = new RefObj;

}

~A()
{
 obj->ReleaseRef();
}
}


RefObj
{

int m_iRefCounter;
RefObj()
{
m_iRefCounter = 1;
}
AddRef()
{
m_iRefCounter++;
}
ReleaseRef()
{
m_iRefCounter--
if(m_iRefCounter == 0)
{
 delete this;
}
}
}

}

Почему никто не упомянул слабые и сильные указатели?
Сильный указатель - это умный указатель, который действует нормально.
Слабый указатель - это умный указатель, который не может удалить себя, если все сильные указатели не находятся в области видимости.
Сильный указатель указывает на владение, слабый указатель указывает на совместное использование.
Посмотрите на boost.shared_ptr и boost.weak_ptr и Loki's StrongPtr для реализации.
Также взгляните на RAII. Если бы вы знали RAII, вы бы знали ответ на этот вопрос сами.

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

(Первый управляемый ресурс, о котором обычно беспокоятся люди, - это память, но все, что может просочиться - память, дескрипторы файлов, указатели IDispatch - должно иметь код, который неявно обрабатывает очистку).

Для управляемых ресурсов, совместно используемых несколькими объектами (почти наверняка, если "этот объект" должен иметь указатель на что-то, что очищается "этим объектом"), вам обычно требуется "указатель с подсчетом ссылок" для управления объект или "слабый указатель", в зависимости от ваших жизненных требований.

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

Книги Scott Meyers Effective C++ были разумной отправной точкой для изучения умных указателей, но на практике вам, вероятно, следует просто взять проверенную библиотеку, такую ​​как Boost, и позволить кому-то еще беспокоиться о получении непонятных угловых случаев (например, что происходит, если конструктор выдает исключение?) верно.

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


class Foo
{
public:
  Foo() {p = new int;}
 ~Foo(){}

private:
 int a;
 int *p;
};

Этот класс имеет 2 члена данных: целое число a и указатель на целое число p, Когда вызывается деструктор, объект уничтожается, что означает, что деструкторы для всех его членов вызываются. Это происходит, даже если тело деструктора пусто. В случае примитивного типа, такого как целое число, вызов его деструктора просто означает, что занимаемая им память будет освобождена. Однако при уничтожении указателя возникает ловушка: все, на что он указывает, по умолчанию не уничтожается. Для этого вы должны явно позвонить delete,

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


~Foo() {delete p};

Итак, возвращаясь к вашему вопросу, все члены вашего класса, которые не являются указателями, будут уничтожены, несмотря ни на что, когда вызывается деструктор объекта. С другой стороны, если у вас есть члены, которые являются указателями, все, на что они указывают, не будет уничтожено, если вы специально не вызовите для них удаление в деструкторе.

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

Прежде всего, это совершенно неразумно?

Я бы не сказал, необоснованно, возможно, сомнительно.

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

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

Другие вопросы по тегам