




Я стараюсь инициализировать значения указателя NULL при создании объекта. Это позволяет проверять NULL, чтобы увидеть, определена ли указательная переменная.
Я согласен, за исключением того, что вы должны использовать 0, а не NULL. см. research.att.com/~bs/bs_faq2.html#null
Или, в C++ 0x, вы должны использовать nullptr.
Я согласен, хотя «я склонен» звучит как «рекомендуется время от времени дышать».
Вы всегда должны инициализировать ваши указатели на NULL в своем конструкторе; таким образом вы можете проверить деструктор, был ли он инициализирован. Кроме того, более эффективно сделать это в списке аргументов конструктора следующим образом:
MyClass::MyClass() : pointer(NULL)
{
}
MyClass::~MyClass()
{
if (pointer != NULL) { delete pointer; }
}
Точно так же вы также должны установить значение NULL при его удалении и проверить значение на null при его выделении, если объект будет повторно инициализирован много раз в течение срока службы вашей программы. Однако установка значения NULL в деструкторе не будет иметь никакого значения, поскольку объект, очевидно, в любом случае будет уничтожен.
Преимущество выполнения этого способа (вместо того, чтобы явно указывать «указатель = NULL;» в конструкторе), заключается в том, что компилятор уже неявно выполняет подобное присваивание за вас. При ручном назначении это происходит дважды, что обычно не имеет большого значения, кроме случаев, когда вы создаете много экземпляров своего класса в большом цикле или что-то в этом роде.
Ваш код бессмысленен на C++, поскольку delete уже включает проверку NULL. Не проверяйте значение null вручную перед удалением!
@Konrad: Я бы не назвал это "бессмысленным", но определенно "лишним" и "ненужным"
Вы не можете, AFAIK я знаю. Стандартный метод - установить для него значение NULL, если он не содержит допустимого значения. Оставлять указатели вокруг этой точки на недопустимую память - плохая практика ни при каких обстоятельствах. Если вы будете придерживаться этого правила, вы всегда можете проверить значение NULL, чтобы узнать, «определено» оно или нет.
В дополнение к проверке на наличие 0 (NULL) одним из решений может быть рефакторинг вашего кода, чтобы вы сила указатель всегда оставался действительным. Это не всегда возможно, но в большинстве случаев это лучшее решение.
В вашем случае (как и в большинстве других случаев) это означает инициализацию указателя в конструкторе (т.е. сразу после начала его жизненного цикла) и его уничтожение в конце его жизненного цикла. Сделайте переменную private и не разрешайте прямой доступ для записи к ней, чтобы она всегда оставалась действительной.
Это часто используемый шаблон в C++, и он эффективно ограничивает время жизни объекта указателя временем жизни вашего класса. Иногда жизнеспособным решением может быть предоставление некоторого типа reset, который удаляет указатель и немедленно повторно инициализирует его. Если это написано безопасным для исключений способом, вы также гарантируете, что ваш указатель никогда не будет недействительным.
Создайте ли нет флаг boolean для отслеживания действительности вашего указателя. Это решение не имеет преимуществ и имеет много недостатков по отношению к установке указателя на 0.
Зачем беспокоиться о проверке значения указателей? Просто инициализируйте его значением нулевого указателя, а затем просто вызовите для него delete. delete по нулевому указателю ничего не делает (это гарантирует стандарт).
class MyClass {
public:
MyClass():pointer(0) { }
~MyClass() {
delete pointer;
pointer = 0;
}
initializePointer() {
pointer = new OtherClass();
}
private:
OtherClass* pointer;
};
И каждый раз, когда вы вызываете для него delete, вы должны установить указатель на значение нулевого указателя. Тогда у вас все в порядке.
Я обычно компилирую, когда все предупреждения рассматриваются как ошибки. У меня возникли ошибки компиляции при удалении указателей с нулевым значением.
Как компилятор узнает, что указатель будет равен нулю во время выполнения?
Томмазо, это звучит непослушно. Я также хотел бы знать, как компилятор может узнать во время компиляции, что содержит указатель во время выполнения.
зачем устанавливать его на 0 после удаления. После вызова деструктора объектов он больше не существует. Поэтому вам не нужно беспокоиться ни о чем другом, ссылаясь на него.
@ Грег Роджерс: здесь это не помогает, но и не повредит. В других ситуациях, когда вместо деструктора удаление происходит в другой части кода, установка указателя на 0 позволит избежать двойного удаления через эту переменную (не через другие указатели).
я не хотел показаться глупым. говоря «вы должны установить для него значение null после его удаления», а затем не делать этого, это нехорошо :) также мы могли бы добавить членов позже, которые могли бы попробовать его использовать и протестировать на ненулевое значение в своих деструкторах. поэтому я делаю «всегда делать его нулевым» вместо «делать нулевым только тогда, когда это необходимо».
Попробую еще раз, возможно, мои ошибки при компиляции были вызваны другими конструкциями.
@dribeas - Хотел бы я дать вам +1 за этот комментарий.
Это было настолько очевидно, что я упустил это! Вы только что решили для меня двухчасовую Heisenbug. Спасибо!
Настоящий отвечать - это отвечать, но я просто хотел сделать дополнительный комментарий. Использование интеллектуальных указателей (в этом случае достаточно std :: auto_ptr) решает проблему и имеет то преимущество, что вам не нужно помнить об удалении указателя в деструкторе. Фактически деструктор по умолчанию (сгенерированный компилятором) позаботится о ресурсах памяти.
Если сделать комментарий еще более общим, ваш класс должен объявить оператор присваивания и конструктор копирования, помеченные как частные (и не определенные) или определенные вручную, чтобы избежать двойного удаления указателя. Компилятор предоставил operator == и конструктор копирования просто скопирует указатель, и в конечном итоге вы попадете в двойное удаление. Я пишу это здесь, так как это тоже нужно учитывать, если вы используете std :: auto_ptr с добавленной странностью семантики копирования в auto_ptr.
Дополнительный ответ, о котором еще никто не упоминал, заключается в использовании объекта интеллектуального указателя вместо необработанного указателя. В зависимости от того, как впоследствии будет использоваться указатель, здесь могут быть уместны std::auto_ptr, boost::shared_ptr или ряд других классов интеллектуальных указателей. boost::scoped_ptr также может работать, если вы немного измените принцип работы initializePointer().
Таким образом, интеллектуальный указатель запоминает, действителен он или нет, и удаляет себя при уничтожении содержащего объекта:
class MyClass {
public:
MyClass();
~MyClass() {
}
initializePointer() {
pointer.reset( new OtherClass());
}
private:
std::auto_ptr<OtherClass> pointer;
};
Весь смысл конструктора в том, что после его завершения все переменные-члены определены правильно. В этом случае NULL является допустимым начальным значением.
Вызов удаления для NULL хорошо определен.
В более обычном случае можно ожидать, что конструктор определит значение указателя RAW. Кроме того, поскольку ваш объект содержит указатель RAW и, очевидно, владеет им (он удаляет его, это подразумевает владение), вы ДОЛЖНЫ также убедиться, что вы переопределяете созданные компилятором версии конструктора копирования и оператора присваивания.
class MyClass
{
public:
MyClass()
:pointer(NULL) // valid value.
{}
~MyClass()
{
delete pointer; // This is fine.
}
void initializePointer() // Missing return type
{
pointer = new OtherClass();
}
private:
MyClass(MyClass const& copy); // If you don't define these
MyClass& operator=(MyClass const& copy);// two the compiler generated ones
// will do nasty things with owned
// RAW pointers.
OtherClass* pointer;
};
В качестве альтернативы вы можете использовать один из стандартных интеллектуальных указателей. Вероятно, std :: auto_ptr <>, если вы действительно не хотите сделать объект копируемым.
Не могли бы вы отредактировать заголовок? похоже, что вы хотите проверить, определена ли переменная (буквально). Но похоже, что вы хотите проверить, содержит ли переменная определенное значение.