2009-10-03 15 views
7

Cała dokumentacja, którą można znaleźć w kontenerach STL (zarówno w kolejce, jak i na liście) mówi, że dla każdej z funkcji usuwania wywoływany jest destruktor obiektu. Oznacza to, że nie mogę używać std :: queue, gdy tylko chcę kolejki, która jest po prostu listą obiektów wymagających jakiejś operacji wykonanej na nich.Usunięcie z STL std :: queue bez niszczenia usuniętego obiektu?

Chcę móc dodawać obiekty do kolejki, gdy czekają w kolejce, abym coś dla nich zrobił. Potem chcę je usunąć, gdy skończę z nimi, bez niszczenia przedmiotowego obiektu. Nie wydaje mi się to możliwe z dokumentacji, którą przeczytałem. Czy błędnie odczytuję dokumentację? Czy istnieje inny typ kolejki w STL inny niż podstawowa "kolejka", która nie wywołuje destruktora obiektu usuniętego w wywołaniu funkcji pop_front?

Edytuj, aby wyjaśnić: W moim przypadku używam listy wskaźników. Coś takiego:

dbObject *someObject; 
    queue<dbObject *> inputQueue; 
    inputQueue.push_back(someObject); 

    ... 

    dbObject *objectWithInput = inputQueue.front(); 
    //handle object's input... 
    inputQueue.pop_front(); // Remove from queue... destroyed now? 
+1

jeśli przechowujesz wskaźniki w kolejce, ich usunięcie nie wywoła destruktora. –

+0

Podobne pytanie: http://stackoverflow.com/questions/1525535/delete-all-items-from-a-c-stdvector –

Odpowiedz

17

Jeśli umieścisz wskaźniki w kolejce (i dowolnym innym pojemniku STL), wskaźniki nie zostaną usunięte po ich usunięciu.

Aby opracować: przy użyciu std :: queue i usunięciu obiektu wywoływany jest destruktor some_obj *. Ale destruktor dla zwykłego wskaźnika (lub dowolnego typu POD - int, char, etc) jest pusty, nie ma op. Cienka linia jest taka, że ​​destruktor dla some_obj * bardzo różni się od destruktora dla some_obj.

+2

Dokładnie. I tylko dla odniesienia, jeśli faktycznie chcesz kiedykolwiek kontener, aby usunąć wskazane obiekty, to musisz albo przechowywać jakiś rodzaj inteligentnego wskaźnika, albo użyć czegoś w rodzaju podpełzaczy-kropek doładowania. – TheUndeadFish

3

Co powiesz na używanie listy wskaźników do obiektów?

+0

nie byłoby to jeszcze nazwać Deconstructor? To właśnie używam, większość moich obiektów to wskaźniki do obiektu przechowywanego gdzie indziej w kodzie. –

4
class someobj_t {}; 

std::queue<someobj_t> q; 
... 

someobj_t ppd = q.front(); // ppd is not a reference 
q.pop(); 

// ppd now contain removed object 

Jeśli nie chcesz someobj_t do skopiowania można użyć std::queue< shared_ptr<someobj_t> >.

+0

Używam listy wskaźników. –

+1

Wtedy prawdziwy obiekt nie zostanie zniszczony podczas wymazywania. –

7

Kontenery STL mają semantykę wartości. Po naciśnięciu obiektu do kontenera STL kontener STL zachowuje jego własną kopię obiektu, a gdy obiekt (kopia wewnętrzna) zostanie usunięty z kontenera, zostaje zniszczony.

Jeśli użyłeś kontenera typu proxy, raw pointers, smart pointers (shared_ptr, weak_ptr) lub adaptery (jako boost :: reference_wrapper), kontener STL zniszczy proxy, ale nie typ. Wybór jednego spośród innych jest zwykle kwestią tego, jak chcesz radzić sobie z zasobami.

Najbardziej popularny idiom używa surowych wskaźników, ale nie określają, kto jest odpowiedzialny za zniszczenie (kod, który ściąga z kontenera, powinien usunąć wskaźnik, lub zasób jest obsługiwany gdzieś indziej?).

Współczesne zastosowanie przesuwa się w kierunku podejścia shared_ptr, ponieważ osłabia problem własności. Obiekt zostanie utrzymany przy życiu po wyjęciu go z kontenera, a jeśli nikt inny nie posiada shared_ptr, obiekt zostanie automatycznie usunięty, gdy lokalny shared_ptr wykracza poza zakres. Użycie weak_ptr zachowuje prawo własności do oryginalnego kodu, ale pozwoli ci sprawdzić ważność wskaźnika (jeśli został usunięty) przed użyciem. Może to pozwolić na uniknięcie wykonywania operacji na obiekcie, który zostanie natychmiast usunięty.

Problem z podejściem shared_ptr/weak_ptr polega na tym, że zmusza cię do użycia shared_ptr do przechowywania oryginalnego zasobu. Oznacza to, że nie będzie można wstawić wskaźnika do podobiektu (atrybutu członka) innej klasy bez przeprojektowania klasy w celu przechowywania atrybutu przez shared_ptr, a to będzie miało inne implikacje (atrybuty nie będą już ciągłe w pamięci będą wymagane bardziej dynamiczne operacje alokacji ...)

Technika, która jest ledwo widoczne jest przy użyciu adapterów jak boost :: reference_wrapper <>. Opakowanie referencyjne to obiekt proxy, który zawiera odniesienie do oryginalnego obiektu i sam może być kopiowany. Przewagą nad prostymi wskaźnikami surowymi jest to, że po odczytaniu kodu jasne jest, że zasób jest zarządzany poza kolejką: kod pobierający dane z kolejki nie musi usuwać obiektu. Zaletą inteligentnego wskaźnika jest to, że nie trzeba przeprojektowywać innych części systemu, aby używać inteligentnych wskaźników. Wadą jest to, że tak jak w podejściu opartym na surowym wskaźniku, należy upewnić się, że czas życia obiektu, którego dotyczyło, przeżywa ręczne odniesienie do odniesienia w kontenerze.

+0

Podoba mi się sposób, w jaki wyjaśniłeś i sformułowałeś tę odpowiedź, bardzo przydatną i bardzo kompletną. – joshperry

2

Pomyśl elementu w istocie kontenerowego „in-zakres” tego pojemnika, gdy element jest usuwany z pojemnika to jest tak jak pozostawiając zakres funkcji. Jeśli zmienna jest wskaźnikiem, nic nie dzieje się z przedmiotem po opuszczeniu zakresu. Jeśli zmienna jest stosem lokalnym, destruktor zostanie automatycznie wywołany po opuszczeniu zasięgu.

Przechowywanie wskaźniki w pojemnikach mają takie same upadki jak przydzielanie do lokalnego surowego wskaźnik, pamięć nie jest czyszczony automatycznie. W funkcji, jeśli nie usuniesz wskaźnika lub nie przeniesiesz własności, zwracając go, masz wyciek pamięci.

Podczas przechowywania surowych wskaźników w pojemniku własność może stać się nieco dwuznaczne i przecieki mogą łatwo zdarzyć. Spójrz na tr1 :: shared_ptr i zapisz je w kontenerach.

std :: unique_ptr w C++ 0x będzie również dobrym rozwiązaniem do przechowywania wskaźnik w pojemniku stdlib gdy jest ona dostępna.

Powiązane problemy