Operator new () zachowuje się inaczej, gdy operator delete () jest usuwany, w zależności od istnienia domyślnego konstruktora

17

Utworzenie nowego obiektu klasy C za pomocą operatora new () powoduje błąd:

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

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


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

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

Ale kiedy wymienić C() {} z C() = default; lub usunąć linię tak, że kompilator wstawia konstruktor domyślny (który moim zdaniem ma taki sam skutek = default), kod zostanie skompilowany i uruchomić.

Jakie są różnice między domyślnym konstruktorem generowanym przez kompilator a domyślnym konstruktorem zdefiniowanym przez użytkownika?

Mam trochę wskazówek w tym poście , ale klasa C tutaj (bez konstruktora podanego przez użytkownika) nie jest trywialna, ponieważ destruktor jest wirtualny, prawda?

Skompilowany z najnowszym Visual Studio, c ++ 17.

yeshjho
źródło
3
Nie jestem pewien, ale myślę, że różnica polega na tym, że domyślnym konstruktorem jestnoexcept
Sebastian Redl
1
Nie można powielać za pomocą g ++. Podobna diagnostyka dotycząca operator delete()tego, czy konstruktor jest zapisywany ręcznie, czy też niejawnie. Co jest zgodne z moimi oczekiwaniami - ponieważ newwyrażenie może generować wyjątek , kompilator musi uzyskać dostęp operator delete().
Peter
@SebastianRedl masz rację, dodanie noexceptspowoduje kompilację kodu, ale jak ...?
yeshjho
1
@Peter Wyjątek może zostać zgłoszony tylko przez konstruktora, więc jeśli jest noexcepttak, jak wspominał SebastianRedl, operator deletenie trzeba dołączać wywołania . Również g ++ narzeka, tylko jeśli destruktor jest wirtualny. W przeciwnym razie zawsze się kompiluje, nawet jeśli konstruktor rzuca.
orzech
@LeDYoM Twój link dotyczy parsowania adresów IP, co wydaje się nie mieć związku z pytaniem. Czy opublikowałeś zły link?
LF

Odpowiedzi:

17

Jakie są różnice między domyślnym konstruktorem generowanym przez kompilator a domyślnym konstruktorem zdefiniowanym przez użytkownika?

newwyrażenie wywołuje odpowiedni, operator newa następnie wywołuje konstruktor. Jeśli konstruktor zgłasza newwyrażenie wyjątku, należy cofnąć efekt operator new(aby uniknąć wycieku pamięci) przez wywołanie odpowiedniego operator delete. Jeśli to ostatnie zostanie usunięte, newwyrażenie nie może go wywołać, co powoduje kompilator error: use of deleted function 'static void C::operator delete(void*)'.

noexceptKonstruktor nie może wyrzucić wyjątek, a tym samym odpowiedni operator deletenie jest konieczne, gdyż nie będzie się przez newwyrażenia. defaultKonstruktor trywialny klasy także noexceptkonstruktor. Obecność wirtualnego destruktora wymaga operator deleteusunięcia, ponieważ wywoływany jest specjalny destruktor usuwający skalar (szczegół implementacji umożliwiający deletewyrażenie poprzez wskaźnik klasy bazowej) operator delete.

Wydaje się, że standard C ++ nie określa, czy kompilator musi wymagać operator deleteusunięcia, nawet jeśli nie można go wywołać przez newwyrażenie. gccJednak nie wydaje się być wywołując odpowiedni operator deletew newekspresji w ogóle czy jest deleted (napisali raport o błędzie ).

Maxim Egorushkin
źródło