delete this;

0

Witam.

dzisiaj w pewnym kodzie znalazłem ciekawe wyrażenie...

....
void Foo::Destroy()
{
    delete this;
}
...

Na początku bardzo się zdziwiłem skąd takie wyrażenie. Pogóglałem odrobinę, popytałem trochę i juz wiem, że jest legalne :) Wiem już też (co jest dość logiczne), że po delete this nie można korzystać z żadnych memberów clasy, gdyż ich już najpewniej po prostu nie ma. Także nie powinno się tego wyrażenia używać w destruktorach, gdyz to spowoduje najpewniej nieskończoną pętlę, lub od razu crash...

Pytania mam do was dwa.
Czy znacie jakaś książkę lub artykół, gdzie ten przypadek jest opisywany? Jakie może jeszcze niesć ze sobą skutki używanie takiego wyrażenia..? Na co trzeba wówczas szczególnie uważać

oraz drugi, to co w sumie bardziej mnie interesuję...
Czy sami potraficie wskazać zastosowania takiego wyrażenia. Czyli kiedy np nie można się bez tego obyć?

Pozdrawiam!

0

Taka konstrukcja wymusza przydział pamięci dla instancji klasy na stercie. Jak utworzysz klasę na stosie to taki kod pewnie wywali ci program.
Ja bym nie stosował takich trików. Jedyne zastosowanie jakie widzę to wtedy gdybyś w jakiś sensowny sposób przeciążył operator delete.

0

no faktycznie...to jest jedna z konsekwencji...

Wszakże po wykonaniu delete this z jakiej kolwiek metody, automatyczne wywołanie destruktora (na skutek opuszczenia zakresu ważności) faktycznie spowoduje kasacje nie naszego juz obszaru pamięci...

Ja bym nie stosował takich trików.

na obecnym etapie mojej znajomości tego, to też bym tego nie stosował, bo nie wiem czym to grozi ;)

Znalazłem to jednak w dość poważnym kodzie, stąd stwierdzam, że to jednak ma swoje poważne zastosowanie ;)

0

A możesz pokazać ten kod?
Możesz również sprawdzić czy detele nie jest przeciążony.

0

Raz użyłem takiej konstrukcji opakowując okno WinAPI w klasę. Klasa okna jest niszczona wraz nadejściem komunikatu WM_DESTROY. Dzięki temu nie musiałem pamiętać wskaźników do klas i ręcznie ich zwalniać.
A oto fragment:

TapWindow::~TapWindow()
{
   mDeleting = true;
   if(mHandle) DestroyWindow(mHandle);
}

LRESULT TapWindow::WindowProc(UINT Msg, WPARAM wParam, LPARAM lParam)
{
   if((Msg >= WM_MOUSEFIRST) && (Msg <= WM_MOUSELAST))
      OnMouseMessage(Msg, lParam, wParam);
   else switch(Msg)
   {
   case WM_DESTROY:
      if(mDeleting) mHandle = NULL;
      else { delete this; return 0; }
      break;
   }
   return CallWindowProc((FARPROC) mOldWndProc, mHandle, Msg, wParam, lParam);
}

Tyle, że nie można było tworzyć tych klas na stosie. Aby wymódz to ograniczenie na kodzie, można ukryć konstruktor (tj. zrobić go chronionym lub prywatnym) i dostarczyć statyczną metodę tworzącą obiekt na stercie. Zamiast tego ograniczenia można objąć delete w try...except.
Jak widać problem destruktora rozwiązałem zmienną 'mDeleting'.
Więcej zagrożeń nie dostrzegam.

0
rnd napisał(a)

Jedyne zastosowanie jakie widzę to wtedy gdybyś w jakiś sensowny sposób przeciążył operator delete.

A po co to przeciążanie? A co powiesz o klasach z licznikiem referencji np. interfejsy COM z IUnknown?

delete this to dość powszechny "trik".

0

Ten "trick" ma ogólnie zastosowanie tam, gdzie istnieje jakaś automatyzacja w zarządzaniu pamięcią wbudowana w same obiekty zarządzane.

0

spotkalem raz w kompilatorze bodajze visual studio 2003 albo 2005 blad, ktory przy pewnej specyficznej konstrukcji klasy oraz kompilacji w trybie debug powodowal ze proba delete this; na poprawnym obiekcie na stercie co prawda sie udawala, ale ... program nie byl w stanie wyjsc z metody w ktorej owo delete this zostalo wykonane. metoda prawidlowo dochodzila do konca, po czym nastepowal crash - program skakal w blizej nie okreslone miejsce pamieci, bynajmniej nie tam gdzie powinien wrocic z metody.. ciekawe czy sobie zachowalem ten kawalek kodu..

//down: kompletnie juz nie pamietam szczegolow, sorry

0

a czy była to metoda virtualna? Albo wywoływana z innej metody virtualnej?

0

Cześć,

Być może moje pytania są dość naiwne, ale...

adf88 napisał(a)

Dzięki temu nie musiałem pamiętać wskaźników do klas i ręcznie ich zwalniać.

Czy chodzi o wskaźniki do klasy TapWindow?
Nie do końca rozumiem sens stosowaniu wspomnianej konstrukcji (tj. delete this). Niezależnie od tego ile masz wskaźników wskazujących na pojedynczy obiekt tej klasy, to jeżeli tenże obiekt został utworzony na stercie, to i tak konieczne i wystarczające jest tylko jednokrotne użycie operatora new. Tutaj możesz zniszczyć okno przekazując funkcji WindowProc WM_DESTROY jako Msg, ale i tak musisz wiedzieć, dla którego konkretnie okna tej klasy masz tą metodę wywołać. Nie dostrzegam tutaj jakichś znacznych korzyści, które miałyby wynikać z tego rozwiązania.
Dlaczego piszesz, że konieczne jest stworzenie statycznej metody tworzącej obiekt na stercie?
Co się stanie jeżeli utworzymy obiekt tej klasy na stosie?
Na czym polega problem destruktora?

0

C++FAQ głosi, że delete this; ma więcej minusów niż plusów.

Crash aplikacji przy deklarowaniu w inny sposób niż dynamicznie jest największym.

Jednak , na przykład, wxWidgets stosuje tę konstrukcję nagminnie, podczas kasowania okien.
Powód dość prosty: zapamiętywanie i manualne kasowanie wszystkich wskaźników to "pain in the ass".

Każde okno zapamiętuje wszystkie swoje child windows ( a w przypadku kontrolek może ich być bardzo dużo ) i jest odpowiedzialna za usunięcie każdego z nich, gdy same okno jest usuwane - stąd virtualna metoda odpowiedzialna za skasowanie okna ( wywołanie do WInApi ) i zwolenienie pamięci ( delete this; ).

Ma to swoje korzyści, na pewno.

A zabezpieczenie przed tworzeniem na stosie obiektów dziedziczących z wxWindow jest dość proste = oczojebne "NIE RÓB TEGO" w dokumentacji...

1 użytkowników online, w tym zalogowanych: 0, gości: 1