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:
template<typename T> class explicit_delete_ref_cnt_ptr {
private:
T* ptr;
int rc;
...
public:
void delete_if_rc0() {
if( this->ptr ) {
this->rc--;
if( this->rc == 0 ) {
delete this->ptr;
}
this->ptr = 0;
}
}
};
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.
class Pointee {
private:
int rc;
...
public:
void delete_if_rc0() {
this->rc--;
if( this->rc == 0 ) {
delete this;
}
}
}
};
W rzeczywistości nie musi to wcale być metoda składowa, ale może być funkcją bezpłatną:
map<void*,int> keepalive_map;
template<typename T>
void delete_if_rc0(T*ptr) {
void* tptr = (void*)ptr;
if( keepalive_map[tptr] == 1 ) {
delete ptr;
}
};
(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.)
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.