Есть ли установленное значение указателя для освобожденного pointee? - PullRequest
6 голосов
/ 20 января 2011

Некоторым программистам нравится устанавливать нулевую переменную указателя после освобождения указателя:

delete ptr;
ptr = 0;

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

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

delete ptr;
ptr = SOME_MAGIC_VALUE;

В идеале я бы хотел, чтобы Visual Studio 2008 сообщала мне « Программа была прервана, потому что вы пытались получить доступ к уже выпущенному pointee здесь! » в режиме отладки.


Ладно, похоже, я должен сделать проверку сам.Что-то не так со следующим шаблоном?

template <typename T>
void sole_delete(T*& p)
{
    if (p)
    {
        delete p;
        p = 0;
    }
    else
    {
        std::cerr << "pointee has already been released!\n";
        abort();
    }
}

Ответы [ 12 ]

7 голосов
/ 20 января 2011

Нет. Проверьте на «0» при попытке delete что-то, если вы действительно хотите предупредить об этом или сделать ошибку.

В качестве альтернативы, во время разработки вы можете опустить ptr = 0; и положиться на valgrind, который сообщит вам, где и когда вы пытаетесь получить двойную свободу Только не забудьте поставить ptr = 0; обратно на выпуск.

Редактировать Да, люди, которых я знаю C ++ не требует теста около delete 0;

Я не предлагаю if (ptr != 0) delete ptr;. Я предлагаю if (ptr == 0) { some user error that the OP asked for } delete ptr;

5 голосов
/ 20 января 2011

Назначить NULL после отпускания указателя. И перед его использованием проверьте на его NULL ity .. Если оно пустое, сообщите об ошибке самостоятельно .

4 голосов
/ 20 января 2011

Есть ли альтернативное значение, которое я мог бы присвоить переменной-указателю, обозначающей освобожденные пуанты?

В идеале я бы хотел, чтобы Visual Studio 2008 сообщала мне: «Программа была прервана, потому что вы пытались получить доступ к уже выпущенному pointee здесь!» в режиме отладки.

Очень вероятно, что вы просто делаете delete ptr. Время выполнения поймает вас, если вы дважды удалите этот указатель.

Во всяком случае, я не думаю, что написал ptr = NULL более, чем несколько раз за последнее десятилетие. Зачем мне это делать? Такой указатель, безусловно, скрыт внутри объекта, деструктор которого удалит объект, на который он ссылается, и после того, как этот деструктор был вызван, указатель также исчезает.

И если из-за некоторых обстоятельств мне понадобится оставить указатель повисать после удаления указателя, я не установлю его на NULL просто потому, что Я бы хотел, чтобы код зависал 1016 * КАК МОЖНО СКОРЕЕ, если бы я дважды удалил. Установка указателя на NULL просто маскирует ошибку .

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

2 голосов
/ 20 января 2011

Если вы просто хотите проверить выделения и удаления, проще всего написать свои собственные глобальные operator new и operator delete и вручную отслеживать все указатели, которые выделены и освобождены.

Конечно, вы также можете использовать существующий инструмент, который сделает это за вас, например, Valgrind.

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

Но еще раз, существующие инструменты, такие как Valgrind, уже сделали это для вас. В случае Valgrind ваш исполняемый файл запускается на виртуальной машине; другие программы исправляют ваше приложение, изменяя байт-код.

2 голосов
/ 20 января 2011

Нет, вызов delete для нулевого указателя совершенно нормален с точки зрения C ++. Присвоение некоторого магического значения приведет к серьезному нарушению кода - теперь вам придется различать нулевые указатели, действительные указатели и магическое значение указатели, и я думаю, это будет огромный беспорядок.

Если вы действительно против удаления нулевого указателя, у вас может быть отдельный логический флаг вместе с каждым указателем, означающим, что это был delete d. Возможно, вы могли бы написать для этого класс-оболочку.

1 голос
/ 20 января 2011

Нет никакого смысла.

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

С некоторым кодом это дает:

T* p = /* something */;
T* q = p;

delete q;
q = 0;

Чувствуете ли вы себя в безопасности?

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

Это похоже на засорение отверстия в сите и надежда, что оно остановит вытекание воды.

1 голос
/ 20 января 2011

Я думаю, что Sharp Bluetooth уже предоставил правильный ответ , но я думаю, что он не смог объяснить это явно:

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

1 голос
/ 20 января 2011

Отвечая на вопрос [sic].

Нет, для выпущенных указателей нет установленного значения.

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

редактировать: конец ответа на исходный вопрос; бродит о двойном удалении

Это действительно зависит от дизайна, если delete на NULL - ошибка, ожидающая своего появления. Во многих случаях это не так. Возможно, вам следует использовать «безопасное удаление», когда это необходимо - или во время отладки? Как это:

template <typename T>
void safe_delete(T*& ptr)
{
  if (ptr == 0)
    throw std::runtime_error("Deleting NULL!");
  delete ptr;
  ptr = 0;
}
1 голос
/ 20 января 2011

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

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

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

T * array[N];
for( i = 0; i < N; ++ i )
{
   array[i] = new T;
}
T* ptr;
for( i = 0; i < N; ++i )
{
  ptr = array[i];
  delete ptr;
}

и, кроме того, что это не лучший способ сделать что-то, я вызываю delete для переменной "ptr" несколько раз, но по разным адресам и явно не является ошибкой.

0 голосов
/ 20 января 2011

Это не ошибка для delete нулевой указатель;по определению он ничего не делает.

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

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

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

ОднакоСказав это, для человека, которыйo склоняется к ультраформально педантичной, это может быть, по крайней мере, эмоционально удовлетворительной идеей для нулевых указателей, например, в std::vector, после delete.Причина в том, что Священный Стандарт, ISO / IEC 14882, позволяет деструктору std::vector делать довольно отвратительные вещи, такие как копирование значений указателя вокруг.И в формально педантичном представлении даже такое копирование недопустимых значений указателя влечет за собой неопределенное поведение.Не то чтобы это практическая проблема.Во-первых, я не знаю абсолютно никакой современной платформы, где копирование имело бы какой-либо вред, а во-вторых, настолько много кода полагается на стандартные контейнеры, ведущие себя разумно, что им просто необходимо: иначе никто бы не использовал такую ​​реализацию.

Ура & hth.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...