Czy jest dozwolone, delete this;
jeśli instrukcja delete jest ostatnią instrukcją, która zostanie wykonana w tej instancji klasy? Oczywiście jestem pewien, że obiekt reprezentowany przez this
-pointer został new
utworzony.
Myślę o czymś takim:
void SomeModule::doStuff()
{
// in the controller, "this" object of SomeModule is the "current module"
// now, if I want to switch over to a new Module, eg:
controller->setWorkingModule(new OtherModule());
// since the new "OtherModule" object will take the lead,
// I want to get rid of this "SomeModule" object:
delete this;
}
Mogę to zrobić?
c++
memory-management
new-operator
delete-operator
self-destruction
Martijn Courteaux
źródło
źródło
delete this
utworzyłeś ścisłe powiązanie między klasą a metodą alokacji używaną do tworzenia obiektów tej klasy. To bardzo kiepski projekt OO, ponieważ najbardziej fundamentalną rzeczą w OOP jest tworzenie autonomicznych klas, które nie wiedzą ani nie dbają o to, co robi ich rozmówca. Dlatego właściwie zaprojektowana klasa nie powinna wiedzieć ani przejmować się tym, jak została przydzielona. Jeśli z jakiegoś powodu potrzebujesz takiego osobliwego mechanizmu, myślę, że lepszym rozwiązaniem byłoby użycie klasy opakowania wokół klasy rzeczywistej i pozwolenie opakowaniu zająć się przydziałem.setWorkingModule
?Odpowiedzi:
C ++ FAQ Lite zawiera specjalny wpis na ten temat
Myślę, że ten cytat ładnie podsumowuje
źródło
Tak,
delete this;
ma zdefiniowane wyniki, o ile (jak zauważyłeś) zapewniasz, że obiekt został przydzielony dynamicznie i (oczywiście) nigdy nie próbujesz użyć obiektu po jego zniszczeniu. Z biegiem lat zadawano wiele pytań na temat tego, co konkretnie mówi standarddelete this;
, a nie usuwania innych wskaźników. Odpowiedź na to pytanie jest dość krótka i prosta: niewiele mówi. Mówi tylko, żedelete
operand musi być wyrażeniem oznaczającym wskaźnik do obiektu lub tablicę obiektów. Wchodzi on w dość szczegółowe informacje na temat takich rzeczy, jak to, jak wymyśla, jaką (jeśli w ogóle) funkcję dealokacji należy wywołać w celu zwolnienia pamięci, ale cała sekcjadelete
(§ [wykr. Usuń]) w ogóle nie wspominadelete this;
konkretnie. W części poświęconej niszczycielom wspomnianodelete this
w jednym miejscu (§ [class.dtor] / 13):Zwykle potwierdza to pogląd, który standard uważa
delete this;
za prawidłowy - gdyby był nieprawidłowy, jego typ nie miałby znaczenia.delete this;
O ile mi wiadomo , to jedyne miejsce, o którym wspomina standard .W każdym razie niektórzy uważają
delete this
paskudny hack i mówią każdemu, kto będzie słuchać, że należy tego unikać. Jednym z najczęściej cytowanych problemów jest trudność zapewnienia, że obiekty klasy są przydzielane tylko dynamicznie. Inni uważają to za całkowicie rozsądny idiom i używają go przez cały czas. Osobiście jestem gdzieś pośrodku: rzadko go używam, ale nie wahaj się, gdy będzie to odpowiednie narzędzie do pracy.Zasadniczo używasz tej techniki z przedmiotem, który ma życie prawie całkowicie własne. Jednym z przykładów, które zacytował James Kanze, był system rozliczeń / śledzenia, nad którym pracował dla firmy telefonicznej. Kiedy zaczynasz dzwonić, coś bierze to pod uwagę i tworzy
phone_call
obiekt. Od tego momentuphone_call
obiekt obsługuje szczegóły połączenia telefonicznego (nawiązywanie połączenia podczas wybierania numeru, dodawanie pozycji do bazy danych, aby powiedzieć, kiedy połączenie zostało nawiązane, być może połączyć więcej osób, jeśli wykonujesz połączenie konferencyjne itp.) kiedy ostatnie osoby w trakcie połączenia się rozłączają,phone_call
obiekt dokonuje ostatecznej księgowości (np. dodaje pozycję do bazy danych, aby powiedzieć, kiedy się rozłączyłeś, aby mogli obliczyć, jak długo trwało twoje połączenie), a następnie sam się niszczy. Żywotnośćphone_call
obiekt opiera się na tym, kiedy pierwsza osoba rozpoczyna połączenie, a kiedy ostatnie osoby opuszczają połączenie - z punktu widzenia reszty systemu jest to w zasadzie całkowicie arbitralne, więc nie można powiązać go z żadnym zakresem leksykalnym w kodzie lub cokolwiek w tym zamówieniu.Dla każdego, kto może dbać o niezawodność tego rodzaju kodowania: jeśli zadzwonisz do, z lub przez prawie dowolną część Europy, istnieje spora szansa, że zostanie to obsłużone (przynajmniej częściowo) za pomocą kodu to robi dokładnie to.
źródło
bool selfDelete
konstruktora, który zostaje przypisany do zmiennej składowej. To prawda, że oznacza to przekazanie programatorowi wystarczającej ilości liny, aby zawiązać w nim pętlę, ale uważam, że jest to lepsze niż wycieki pamięci.this
. Tak, kod jest obsługiwany przez dokładniethis
. ;)Jeśli cię to przeraża, istnieje całkowicie legalny hack:
Myślę, że
delete this
jest to idiomatyczny C ++ i przedstawiam to tylko jako ciekawość.Istnieje przypadek, w którym ta konstrukcja jest rzeczywiście przydatna - możesz usunąć obiekt po zgłoszeniu wyjątku, który wymaga danych elementu z obiektu. Obiekt pozostaje ważny do momentu wykonania rzutu.
Uwaga: jeśli używasz kompilatora starszego niż C ++ 11, którego możesz użyć
std::auto_ptr
zamiaststd::unique_ptr
, zrobi to samo.źródło
unique_ptr
z innegounique_ptr
wymaga ruchu, ale nie z surowego wskaźnika. Chyba, że coś się zmieni w C ++ 17?Jednym z powodów zaprojektowania C ++ było ułatwienie ponownego użycia kodu. Ogólnie C ++ powinien być napisany tak, aby działał niezależnie od tego, czy klasa jest tworzona na stercie, w tablicy, czy na stosie. „Usuń to” jest bardzo złą praktyką kodowania, ponieważ zadziała tylko wtedy, gdy na stercie zostanie zdefiniowana pojedyncza instancja; i lepiej, żeby nie było innej instrukcji usuwania, która jest zwykle używana przez większość programistów do czyszczenia sterty. Robi to również przy założeniu, że żaden programista konserwacji w przyszłości nie wyleczy fałszywie postrzeganego wycieku pamięci poprzez dodanie instrukcji delete.
Nawet jeśli wiesz z góry, że Twoim obecnym planem jest przydzielenie tylko jednej instancji na stosie, co się stanie, jeśli jakiś przyszły programista pojawi się w przyszłości i zdecyduje się utworzyć instancję na stosie? Albo co, jeśli pokroi i wklei pewne części klasy do nowej klasy, którą zamierza wykorzystać na stosie? Kiedy kod osiągnie „usuń to”, to on zgaśnie i usunie go, ale wtedy, gdy obiekt wykroczy poza zakres, wywoła destruktor. Destruktor spróbuje go ponownie usunąć, a następnie zostaniesz powstrzymany. W przeszłości zrobienie czegoś takiego zepsułoby nie tylko program, ale także system operacyjny i komputer wymagałyby ponownego uruchomienia. W każdym razie jest to zdecydowanie NIE zalecane i prawie zawsze należy tego unikać. Musiałbym być zdesperowany, poważnie otynkowany,
źródło
Jest to dozwolone (po prostu nie używaj później tego obiektu), ale nie napisałbym takiego kodu w praktyce. Myślę, że
delete this
powinien pojawić się tylko w funkcji, które nazywa sięrelease
alboRelease
i wygląda następująco:void release() { ref--; if (ref<1) delete this; }
.źródło
Cóż, w modelu Component Object Model (COM)
delete this
konstrukcja może być częściąRelease
metody, która jest wywoływana za każdym razem, gdy chcesz zwolnić obiekt akwizytowany:źródło
To jest podstawowy idiom dla obiektów zliczanych przez referencje.
Liczenie referencji jest silną formą deterministycznego zbierania śmieci - zapewnia, że obiekty zarządzają swoim własnym czasem życia, zamiast polegać na „inteligentnych” wskaźnikach itp. Dostęp do obiektu bazowego jest zawsze możliwy tylko poprzez inteligentne wskaźniki „Referencyjne”, zaprojektowane w taki sposób, aby wskaźniki zwiększały i zmniejszały liczbę całkowitą elementu (liczbę referencji) w rzeczywistym obiekcie.
Kiedy ostatnie odniesienie spadnie ze stosu lub zostanie usunięte, liczba odniesień spadnie do zera. Domyślnym zachowaniem Twojego obiektu będzie wówczas wezwanie do „usunięcia tego” w celu wyrzucania elementów bezużytecznych - biblioteki, które piszę, zapewniają chronione wirtualne wywołanie „CountIsZero” w klasie podstawowej, dzięki czemu możesz nadpisać to zachowanie w przypadku buforowania.
Kluczem do uczynienia tego bezpiecznym nie jest umożliwienie użytkownikom dostępu do KONSTRUKTORA danego obiektu (uczynienie go chronionym), ale zamiast tego wywołują jakiegoś statycznego członka - FABRYCZNĄ „statyczną referencję CreateT (...)”. W ten sposób WIESZ na pewno, że zawsze są one zbudowane ze zwykłym „nowym” i że nigdy nie jest dostępny żaden surowy wskaźnik, więc „usuń to” nigdy nie wybuchnie.
źródło
Możesz to zrobić. Nie możesz jednak do tego przypisać. Dlatego powód, dla którego to robisz: „Chcę zmienić pogląd”, wydaje się bardzo wątpliwy. Moim zdaniem lepszym sposobem byłoby zastąpienie tego obiektu przez obiekt, który trzyma widok.
Oczywiście używasz obiektów RAII, więc nie musisz wcale w ogóle wywoływać usuwania ... prawda?
źródło
To stare pytanie, na które udzielono odpowiedzi, ale @Alexandre zapytał „Dlaczego ktoś miałby to zrobić?” I pomyślałem, że mogę podać przykładowe zastosowanie, które rozważam tego popołudnia.
Starszy kod. Używa nagich wskaźników Obj * obj z usuwaniem obj na końcu.
Niestety czasami potrzebuję, nie często, utrzymać obiekt przy życiu dłużej.
Zastanawiam się, czy to inteligentny wskaźnik liczony jako odniesienie. Ale byłoby wiele kodu do zmiany, gdybym miał używać go
ref_cnt_ptr<Obj>
wszędzie. A jeśli wymieszasz nagiego Obj * i ref_cnt_ptr, możesz niejawnie usunąć obiekt, gdy ostatni ref_cnt_ptr zniknie, nawet jeśli Obj * wciąż żyje.Zastanawiam się więc nad stworzeniem jawnego_delete_ref_cnt_ptr. Tj. Wskaźnik zliczający odniesienie, w którym usuwanie odbywa się tylko w jawnej procedurze usuwania. Używanie go w jednym miejscu, w którym istniejący kod zna czas życia obiektu, a także w moim nowym kodzie, który utrzymuje obiekt dłużej.
Zwiększanie i zmniejszanie liczby odwołań, gdy manipulujesz jawnie_delete_ref_cnt_ptr.
Ale NIE zwalnia, gdy liczba referencyjna jest widoczna jako zero w destruktorze jawnie_delete_ref_cnt_ptr.
Uwalnianie tylko wtedy, gdy liczba referencyjna jest widoczna jako zero w jawnej operacji podobnej do usuwania. Np. W czymś takim jak:
OK, coś takiego. To trochę niezwykłe, że typ wskaźnika liczonego w referencjach nie usuwa automatycznie obiektu wskazanego w rc'ed ptr destructor. Wygląda jednak na to, że mieszanie nagich wskaźników i wskaźników rc może być nieco bezpieczniejsze.
Ale do tej pory nie trzeba tego usuwać.
Ale wtedy przyszło mi do głowy: jeśli obiekt, na który wskazywał, pointee, wie, że jest liczony jako referencja, np. Jeśli liczba znajduje się w obiekcie (lub w innej tabeli), wówczas procedura delete_if_rc0 może być metodą obiekt pointee, a nie (inteligentny) wskaźnik.
W rzeczywistości nie musi to wcale być metoda składowa, ale może być funkcją bezpłatną:
(BTW, wiem, że kod nie jest całkiem poprawny - staje się mniej czytelny, jeśli dodam wszystkie szczegóły, więc zostawiam go w ten sposób.)
źródło
Usuń to jest legalne, dopóki obiekt jest w stercie. Trzeba będzie wymagać, aby obiekt był tylko stertą. Jedynym sposobem, aby to zrobić, jest zabezpieczenie destruktora - w ten sposób usuwanie może być wywoływane TYLKO z klasy, więc potrzebujesz metody, która zapewni usunięcie
źródło