Правильно ли я сказал, что мне нужно удалить указатель в деструкторе?
Всякий раз, когда проектируете такой объект, вам сначала нужно ответить на вопрос: владеет ли объект памятью, на которую указывает этот указатель?Если да, то, очевидно, деструктор объекта должен очистить эту память, так что да, он должен вызвать delete.И это, похоже, ваше намерение в отношении данного кода.
Однако в некоторых случаях вы можете захотеть иметь указатели, которые ссылаются на другие объекты, чья жизнь должна управляться чем-то другим.В этом случае вы не хотите вызывать delete, потому что это является обязанностью какой-то другой части программы.Кроме того, это изменяет весь последующий дизайн, который входит в конструктор копирования и оператор присваивания.
Я продолжу отвечать на остальные вопросы в предположении, что вы хотите, чтобы каждый объект TreeNode имел право собственности налевый и правый объекты.
Это правильный способ реализации конструктора по умолчанию?
Нет.Вам нужно инициализировать указатели left
и right
в NULL (или 0, если вы предпочитаете).Это необходимо, потому что неинициализированные указатели могут иметь любое произвольное значение.Если ваш код по умолчанию создает TreeNode, а затем уничтожает его, не назначая ничего указателям, тогда будет вызываться delete для любого начального значения.Так что в этом проекте, если эти указатели ни на что не указывают, вы должны гарантировать, что они установлены в NULL.
Как назначить переменную указателя в конструкторе копирования?Способ, который я написал в конструкторе копирования, может быть неправильным.
Строка left = new TreeNode();
создает новый объект TreeNode и устанавливает left
для указания на него.Строка left = node.left;
переназначает этот указатель так, чтобы он указывал на любой объект TreeNode, на который указывает node.left
.Есть две проблемы с этим.
Проблема 1: Ничто теперь не указывает на этот новый TreeNode.Он теряется и становится утечкой памяти, потому что ничто не может его уничтожить.
Проблема 2: Теперь и left
, и node.left
в конечном итоге указывают на один и тот же TreeNode.Это означает, что объект, создаваемый при копировании, и объект, от которого он получает значения, будут думать, что они владеют тем же TreeNode, и оба будут вызывать delete в своих деструкторах.Вызов delete дважды для одного и того же объекта всегда является ошибкой и вызовет проблемы (в том числе, возможно, сбои или повреждение памяти).
Поскольку каждый TreeNode имеет свой левый и правый узлы, то, вероятно, наиболее разумным является сделатькопии.Поэтому вы должны написать что-то похожее на:
TreeNode::TreeNode(const TreeNode& node)
: left(NULL), right(NULL)
{
data = node.data;
if(node.left)
left = new TreeNode(*node.left);
if(node.right)
right = new TreeNode(*node.right);
}
Нужно ли реализовывать один и тот же код (кроме возврата) как для конструктора копирования, так и для оператора =?
Почти наверняка.Или, по крайней мере, код в каждом должен иметь одинаковый конечный результат.Было бы очень странно, если бы структура копирования и назначение имели разные эффекты.
РЕДАКТИРОВАТЬ - вышеупомянутый параграф должен быть: Код в каждом должен иметь тот же конечный результат, что данные копируются из другогообъект.Это часто будет включать очень похожий код.Однако оператору присваивания, вероятно, необходимо проверить, было ли что-либо уже присвоено left
и right
, и очистить их.Как следствие, может также потребоваться следить за самопредставлением или написать его так, чтобы во время самоназначения не происходило ничего плохого.
На самом деле, есть способы реализоватьодин использует другой, так что фактический код, который манипулирует переменными-членами, написан только в одном месте.Об этом уже говорили другие вопросы по SO, такие как this .