Оператор new () ведет себя по-разному, когда оператор delete () удаляется в зависимости от существования конструктора по умолчанию

17

Создание нового объекта класса C с оператором new () выдает здесь ошибку:

class C
{
public:
    C() {}
    virtual ~C() {}

    void operator delete(void*) = delete;
};


int main()
{
    C* c = new C;
}

с C2280: 'void C::operator delete(void *)': function was explicitly deleted

Но когда я заменяю C() {} с C() = default; или удалить строку , так что компилятор вставляет конструктор по умолчанию (который я считаю , имеет тот же эффект с = default), код будет компиляция и запуск.

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

Я получил некоторую подсказку в этой публикации , но класс C здесь (без предоставленного пользователем конструктора) не тривиален, так как деструктор является виртуальным, верно?

Скомпилировано с последней версией Visual Studio, c ++ 17.

yeshjho
источник
3
Не уверен, но я думаю, что разница в том, что дефолтный конструкторnoexcept
Себастьян Редл
1
Не могу воспроизвести с g ++. Аналогичная диагностика относительно operator delete()того, написан ли конструктор вручную или неявно сгенерирован. Что согласуется с моими ожиданиями - поскольку выражение может вызывать исключение new, компилятору необходим доступ operator delete().
Питер
@SebastianRedl Вы правы, добавление noexceptсделает компиляцию кода, но как ...?
yeshjho
1
@Peter Исключение может быть сгенерировано только конструктором, поэтому, если это так, noexceptкак упомянул SebastianRedl, вызов operator deleteне должен быть включен. Также g ++ только жалуется, если деструктор является виртуальным. В противном случае он всегда компилируется, даже если конструктор выбрасывает.
грецкий орех
@LeDYoM Ваша ссылка предназначена для разбора IP-адресов, что, похоже, не имеет отношения к вопросу. Вы разместили неправильную ссылку?
LF

Ответы:

17

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

newВыражение вызывает соответствующий operator newи затем вызывает конструктор. Если конструктор выдает newвыражение исключения, необходимо отменить эффект operator new(чтобы избежать утечки памяти) путем вызова соответствующего operator delete. Если последнее удалено, newвыражение не может вызвать его, что приводит к компилятору error: use of deleted function 'static void C::operator delete(void*)'.

noexceptКонструктор не может выбросить исключение, следовательно, соответствующее operator deleteне является необходимым , поскольку это не будет вызываться с помощью newвыражения. defaultКонструктор тривиального класса также noexceptконструктор. Присутствие виртуального деструктора требует, operator deleteчтобы его не удаляли, потому что вызывается специальный скалярный деструктор удаления (деталь реализации, обеспечивающая deleteвыражение через указатель базового класса) operator delete.

Кажется, стандарт C ++ не указывает, должен ли компилятор требовать, operator deleteчтобы его не удаляли, даже если он не может быть вызван newвыражением. gccоднако, похоже, что он вообще не вызывает соответствующее выражение operator deleteв newвыражении deleted (опубликовал отчет об ошибке ).

Максим Егорушкин
источник