Próbowałem wezwać ::delete
klasę operator delete
. Ale destruktor nie jest nazywany.
I zdefiniowano klasę MyClass
którego operator delete
został przeciążony. Globalny operator delete
jest również przeciążony. Przeciążonej operator delete
z MyClass
wezwie przeciążonej globalny operator delete
.
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
Dane wyjściowe to:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Rzeczywisty:
Jest tylko jedno wywołanie destruktora przed wywołaniem przeciążony operator delete
od MyClass
.
Spodziewany:
Istnieją dwa połączenia z destruktorem. Jeden przed wywołaniem przeciążony operator delete
od MyClass
. Kolejny przed wezwaniem globalnego operator delete
.
c++
delete-operator
wykreślić
źródło
źródło
MyClass::operator new()
powinien przydzielić pamięć surową (przynajmniej)size
bajtów. Nie powinna próbować całkowicie zbudować instancjiMyClass
. KonstruktorMyClass
jest wykonywany poMyClass::operator new()
. Następniedelete
wyrażenie wmain()
wywołuje destruktor i zwalnia pamięć (bez ponownego wywoływania destruktora).::delete p
Wyrażenie nie posiada informacji na temat rodzaju obiektówp
wskazuje na, ponieważp
jestvoid *
, więc nie może wywołać destruktor.::delete p;
powoduje niezdefiniowane zachowanie, ponieważ typ*p
nie jest taki sam jak typ usuwanego obiektu (ani klasa bazowa z wirtualnym destruktorem)void*
ponieważ operand jest nawet wyraźnie źle sformułowany. [expr.delete] / 1 : " Operand będzie wskazywał na typ obiektu lub typ klasy. [...] Oznacza to, że obiektu nie można usunąć za pomocą wskaźnika typu void, ponieważ void nie jest typem obiektu. * „@OP Poprawiłem swoją odpowiedź.Odpowiedzi:
Nadużywacie
operator new
ioperator delete
. Operatory te to funkcje alokacji i dezalokacji. Nie ponoszą odpowiedzialności za konstruowanie lub niszczenie obiektów. Są one odpowiedzialne tylko za zapewnienie pamięci, w której obiekt zostanie umieszczony.Globalne wersje tych funkcji to
::operator new
i::operator delete
.::new
i::delete
są nowymi / delete-wyrażeniami, podobnie jaknew
/delete
, różniącymi się od nich::new
i::delete
omijają specyficzne dla klasyoperator new
/operator delete
przeciążenia.Nowe / delete-wyrażenia konstruują / niszczą i przydzielają / zwalniają (przez wywołanie odpowiedniego
operator new
luboperator delete
przed budową lub po zniszczeniu).Ponieważ przeciążenie jest odpowiedzialne tylko za część alokacji / dezalokacji, powinno ono zadzwonić,
::operator new
a::operator delete
zamiast::new
i::delete
.delete
Wdelete myClass;
jest odpowiedzialny za wywołanie destruktora.::delete p;
nie wywołuje destruktora, ponieważp
ma typvoid*
i dlatego wyrażenie nie może wiedzieć, który wywołać destruktor. Prawdopodobnie zadzwoni::operator delete
do zastąpionego w celu zwolnienia pamięci, chociaż użycievoid*
argumentu as do wyrażenia kasowania jest źle sformułowane (patrz edycja poniżej).::new MyClass();
wywołuje zastąpiony w::operator new
celu przydzielenia pamięci i konstruuje w nim obiekt. Wskaźnik do tego obiektu jest zwracany jakovoid*
do nowego wyrażenia wMyClass* myClass = new MyClass();
, które następnie skonstruuje inny obiekt w tej pamięci, kończąc żywotność poprzedniego obiektu bez wywoływania jego destruktora.Edytować:
Dzięki komentarzowi @ MM do pytania zdałem sobie sprawę, że
void*
operand jako::delete
jest właściwie źle sformułowany. ( [expr.delete] / 1 ) Jednak wydaje się, że główne kompilatory zdecydowały się tylko ostrzec o tym, a nie o błędzie. Zanim to było robione źle sformułowane, używając::delete
navoid*
już wcześniej niezdefiniowanych zachowań, patrz na to pytanie .Dlatego twój program jest źle sformułowany i nie masz żadnej gwarancji, że kod faktycznie zrobi to, co opisałem powyżej, jeśli nadal będzie mógł się skompilować.
Jak wskazał @SanderDeDycker poniżej jego odpowiedzi, masz również niezdefiniowane zachowanie, ponieważ konstruując w pamięci inny obiekt, który już zawiera
MyClass
obiekt, bez uprzedniego wywołania destruktora tego obiektu, naruszasz [basic.life] / 5, co zabrania tego, jeśli program zależy od skutków ubocznych niszczyciela. W takim przypadkuprintf
wypowiedź w destruktorze wywołuje taki efekt uboczny.źródło
Przeciążenia specyficzne dla klasy są wykonywane nieprawidłowo. Widać to w wynikach: konstruktor jest wywoływany dwukrotnie!
W przypadku klasy
operator new
zadzwoń bezpośrednio do operatora globalnego:Podobnie w przypadku klasy
operator delete
wykonaj:Więcej informacji znajduje się na
operator new
stronie odniesienia.źródło
Zobacz odniesienie do CPP :
Usuń (i nowe) są odpowiedzialne tylko za część „zarządzanie pamięcią”.
Jest więc jasne i oczekiwane, że destruktor jest wywoływany tylko raz - w celu wyczyszczenia instancji obiektu. Gdyby został wywołany dwukrotnie, każdy niszczyciel musiałby sprawdzić, czy został już wywołany.
źródło
operator delete()
funkcja nie jest tym samym, co wyrażenie usuwania. Destruktor jest wywoływany przed wywołaniemoperator delete()
funkcji.