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

Почему кто-либо не говорил [приблизительно 110] turbogears, web.py и опоры ?

16
задан Unihedron 10 August 2014 в 14:25
поделиться

7 ответов

Я помню что-то похожее на оператор delete некоторое время назад в comp.lang.c ++. moderated. Я не могу найти его сейчас, но в ответе говорилось что-то вроде этого ..

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

И Джеймс Канце конкретно сказал:

Это по-прежнему ответственность оператор delete (или delete []) для проверить; стандарт не гарантирует что ему не будет дан нулевой указатель; стандарт требует, чтобы это был no-op, если указан нулевой указатель. Или это реализации разрешено вызывать Это. Согласно последнему проекту, "Значение первого аргумента передается в функцию освобождения может быть значением нулевого указателя; если так, и если функция освобождения один входит в стандартную библиотеку, звонок не имеет никакого эффекта. "Я не совсем уверен, каковы последствия этого " один входит в стандартную библиотеку " предназначены для понимания --- буквально, поскольку его функция не предусмотрена стандартной библиотекой предложение похоже, не применимо. Но как-то, это не имеет смысла

Я помню этот случай, когда-то у меня была похожая проблема, и я сохранил ответ в файле .txt.

UPDATE-1:

О, я нашел его здесь . Также прочтите ссылку отчет о дефекте . Итак, ответ Не указан . Глава 5.3.5 / 7 .

21
ответ дан 30 November 2019 в 16:42
поделиться

У меня нет хорошего ответа, но я немного упростил вопрос. Следующий код удаляет оператор new и обработку исключений:

#include <iostream>
using namespace std;

class Widget {

  public:
    Widget() {
        cout<<"Widget()"<<endl;
    }
    ~Widget() {
        cout<<"~Widget()"<<endl;
    }

  void operator delete(void *v) {
       cout << "operator delete" << endl;
  }
};

int main() {
    Widget* w = 0;
    cout << "calling delete" << endl;
    delete w;
}

Он по-прежнему демонстрирует то же поведение и так, как на VC ++, так и на g ++.

Конечно, удаление указателя NULL не выполняется, поэтому компилятор не выполняет необходимо вызвать оператора удаления. Если действительно выделить объект:

    Widget* w = new Widget;

, тогда все будет работать, как ожидалось.

3
ответ дан 30 November 2019 в 16:42
поделиться

Хотел бы оставить комментарий вместо ответа, недостаточно прав для нового члена.

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

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

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

3
ответ дан 30 November 2019 в 16:42
поделиться

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

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

4
ответ дан 30 November 2019 в 16:42
поделиться

Деструктор объекта вызывается перед оператором удаления. Итак, я предполагаю, что он пытается вызвать деструктор, понимая, что указатель равен NULL, поэтому

  1. не вызывает деструктор, которому нужен экземпляр
  2. , останавливает там операцию удаления (своего рода оптимизация скорости IMHO).

Как сказал Нил, если w содержит виджет, он должен работать.

-1
ответ дан 30 November 2019 в 16:42
поделиться

Вы пытались удалить ПУСТОЙ указатель. Итак, деструктор не был вызван.

class Widget
{   
public:        
    Widget()
    {            
        cout<<"Widget()"<<endl;        
    }       

    ~Widget() 
    {          
        cout<<"~Widget()"<<endl;    
    }    

    void* operator new(size_t sz) throw(bad_alloc) 
    {      
        cout<<"operator new"<<endl;  
        return malloc(sizeof(Widget));
        //throw bad_alloc();    
    }  

    void operator delete(void *v)
    {               
        cout<<"operator delete"<<endl;   
    }
};

int main()
{

    Widget* w = NULL; 
    try 
    {   
        w = new Widget();
        //throw bad_alloc();
    }   
    catch(bad_alloc) 
    {        
        cout<<"Out of Memory"<<endl;  
    }   
    delete w; 
}

Вывод:

operator new
Виджет ()
~ Виджет ()
оператор delete

-2
ответ дан 30 November 2019 в 16:42
поделиться

Прежде всего, это действительно можно упростить до delete (Widget *) 0 - все остальное в вашем main () не нужно воспроизвести это.

Это артефакт генерации кода, который происходит потому, что 1) определяемый пользователем оператор delete должен иметь возможность обрабатывать значения NULL, и 2) компилятор пытается сгенерировать наиболее оптимальный из возможных кодов.

Сначала давайте рассмотрим случай, когда не задействован пользовательский деструктор. В этом случае код для запуска в экземпляре отсутствует, за исключением , оператора delete . Нет смысла проверять значение null перед передачей управления оператору delete , потому что последний в любом случае должен выполнить проверку; и поэтому компилятор просто генерирует безусловный вызов оператора delete (и вы видите, что последний выводит сообщение).

Теперь был определен второй случай - деструктор. Это означает, что ваш оператор delete фактически расширяется на два вызова - деструктор и оператор delete . Но деструктор не может быть безопасно вызван для нулевого указателя, потому что он может попытаться получить доступ к полям класса (компилятор может выяснить, что ваш конкретный деструктор на самом деле этого не делает, и поэтому его можно безопасно вызывать с помощью null this , но похоже, что на практике они не мешают). Таким образом, он вставляет туда нулевую проверку перед вызовом деструктора. И как только проверка уже существует, она может также использовать ее, пропустив вызов operator delete - в конце концов, это в любом случае должно быть запретным, и это избавит от дополнительной бессмысленной проверки на null внутри самого оператора , delete , в случае, если указатель действительно равен нулю.

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

9
ответ дан 30 November 2019 в 16:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: