Projektowanie nowego systemu od podstaw. Będę używać STL do przechowywania list i map pewnych długotrwałych obiektów.
Pytanie: Czy powinienem upewnić się, że moje obiekty mają konstruktory kopiujące i przechowują kopie obiektów w moich kontenerach STL, czy też ogólnie lepiej jest samodzielnie zarządzać życiem i zakresem i po prostu przechowywać wskaźniki do tych obiektów w moich kontenerach STL?
Zdaję sobie sprawę, że jest to trochę krótkie w szczegółach, ale szukam „teoretycznej” lepszej odpowiedzi, jeśli ona istnieje, ponieważ wiem, że oba te rozwiązania są możliwe.
Dwie bardzo oczywiste wady zabawy ze wskaźnikami: 1) Muszę samodzielnie zarządzać alokacją / cofnięciem alokacji tych obiektów w zakresie poza STL. 2) Nie mogę utworzyć obiektu tymczasowego na stosie i dodać go do moich kontenerów.
Czy jest coś jeszcze, czego mi brakuje?
Odpowiedzi:
Ponieważ ludzie wbijają się w skuteczność używania wskaźników.
Jeśli rozważasz użycie std :: vector i jeśli jest niewiele aktualizacji, a często iterujesz po swojej kolekcji i nie jest to typ polimorficzny, „kopie” przechowujące obiekty będą bardziej efektywne, ponieważ uzyskasz lepszą lokalizację odniesienia.
Otoh, jeśli aktualizacje są powszechne, wskaźniki przechowywania pozwolą zaoszczędzić koszty kopiowania / przenoszenia.
źródło
To naprawdę zależy od twojej sytuacji.
Jeśli twoje obiekty są małe, a wykonanie kopii obiektu jest lekkie, przechowywanie danych w kontenerze STL jest moim zdaniem proste i łatwiejsze w zarządzaniu, ponieważ nie musisz się martwić o zarządzanie okresem życia.
Jeśli obiekty są duże, a posiadanie domyślnego konstruktora nie ma sensu lub kopie obiektów są drogie, to prawdopodobnie najlepszym rozwiązaniem jest przechowywanie za pomocą wskaźników.
Jeśli zdecydujesz się używać wskaźników do obiektów, zapoznaj się z biblioteką kontenerów wskaźników zwiększających . Ta biblioteka przyspieszająca opakowuje wszystkie kontenery STL do użytku z dynamicznie przydzielanymi obiektami.
Każdy kontener wskaźników (na przykład ptr_vector) przejmuje własność obiektu, gdy jest dodawany do kontenera i zarządza okresem istnienia tych obiektów za Ciebie. Masz również dostęp do wszystkich elementów w kontenerze ptr_ przez odniesienie. Dzięki temu możesz robić takie rzeczy jak
Te klasy zawijają kontenery STL i współpracują ze wszystkimi algorytmami STL, co jest naprawdę przydatne.
Istnieją również narzędzia do przenoszenia własności wskaźnika w kontenerze na wywołującego (za pomocą funkcji zwalniania w większości kontenerów).
źródło
Jeśli przechowujesz obiekty polimorficzne, zawsze musisz używać kolekcji wskaźników klasy bazowej.
Oznacza to, że jeśli planujesz przechowywać w swojej kolekcji różne typy pochodne, musisz przechowywać wskaźniki lub zostać zjedzonym przez krojącego demona.
źródło
Przepraszam, że skaczę za 3 lata po wydarzeniu, ale uwaga tutaj ...
W moim ostatnim dużym projekcie centralna struktura danych była zbiorem dość prostych obiektów. Około roku po rozpoczęciu projektu, wraz z ewolucją wymagań, zdałem sobie sprawę, że obiekt faktycznie musi być polimorficzny. Potrzeba było kilku tygodni trudnej i nieprzyjemnej operacji mózgu, aby naprawić strukturę danych tak, aby była zbiorem wskaźników klasy bazowej i poradzić sobie ze wszystkimi uszkodzeniami ubocznymi podczas przechowywania obiektów, rzutowania i tak dalej. Kilka miesięcy zajęło mi przekonanie się, że nowy kod działa. Nawiasem mówiąc, to skłoniło mnie do zastanowienia się, jak dobrze zaprojektowany jest model obiektowy C ++.
W moim obecnym dużym projekcie moja centralna struktura danych to zestaw dość prostych obiektów. Około roku po rozpoczęciu projektu (co dzieje się dzisiaj) zdałem sobie sprawę, że obiekt musi być faktycznie polimorficzny. Wróciłem do sieci, znalazłem ten wątek i odnalazłem łącze Nicka do biblioteki kontenerów wskaźników Boost. To jest dokładnie to, co musiałem napisać ostatnim razem, aby wszystko naprawić, więc tym razem spróbuję.
W każdym razie morał dla mnie: jeśli twoja specyfikacja nie jest w 100% odlana z kamienia, idź po wskazówki, a możesz potencjalnie zaoszczędzić sobie dużo pracy później.
źródło
Dlaczego nie wykorzystać tego, co najlepsze z obu światów: zrób kontener inteligentnych wskaźników (takich jak
boost::shared_ptr
lubstd::shared_ptr
). Nie musisz zarządzać pamięcią i nie musisz zajmować się dużymi operacjami kopiowania.źródło
Generalnie przechowywanie obiektów bezpośrednio w kontenerze STL jest najlepsze, ponieważ jest najprostsze, najbardziej wydajne i najłatwiejsze w użyciu obiektu.
Jeśli sam obiekt ma składnię niepodlegającą kopiowaniu lub jest abstrakcyjnym typem podstawowym, będziesz musiał przechowywać wskaźniki (najłatwiej jest użyć shared_ptr)
źródło
Wydaje się, że dobrze rozumiesz różnicę. Jeśli obiekty są małe i łatwe do skopiowania, to jak najbardziej je przechowuj.
Jeśli nie, pomyślałbym o przechowywaniu inteligentnych wskaźników (nie auto_ptr, inteligentnego wskaźnika liczącego ref) do tych, które przydzielasz na stercie. Oczywiście, jeśli zdecydujesz się na inteligentne wskaźniki, nie możesz przechowywać obiektów przydzielonych do tymczasowego stosu (jak powiedziałeś).
@ Torbjörn mówi o krojeniu.
źródło
one
doanother
spowoduje zwolnienie odniesienia zone
i zmianęone
.Używanie wskaźników będzie bardziej wydajne, ponieważ kontenery będą kopiować tylko wskaźniki wokół zamiast pełnych obiektów.
Jest tutaj kilka przydatnych informacji na temat kontenerów STL i inteligentnych wskaźników:
Dlaczego używanie std :: auto_ptr <> ze standardowymi kontenerami jest błędne?
źródło
Jeśli obiekty mają odnosić się do innego miejsca w kodzie, zapisz w wektorze boost :: shared_ptr. Zapewnia to, że wskaźniki do obiektu pozostaną prawidłowe, jeśli zmienisz rozmiar wektora.
To znaczy:
Jeśli nikt inny nie przechowuje wskaźników do obiektów lub lista nie rośnie i nie zmniejsza się, po prostu przechowuj jako zwykłe stare obiekty:
źródło
To pytanie od jakiegoś czasu mnie dręczy.
Skłaniam się do przechowywania wskaźników, ale mam pewne dodatkowe wymagania (opakowania SWIG lua), które mogą Cię nie dotyczyć.
Najważniejszym punktem w tym poście jest przetestowanie przy użyciu swoich obiektów
Zrobiłem to dzisiaj, aby przetestować szybkość wywoływania funkcji składowej na kolekcji 10 milionów obiektów, 500 razy.
Funkcja aktualizuje x i y na podstawie xdir i ydir (wszystkie zmienne typu float).
Użyłem std :: list do przechowywania obu typów obiektów i stwierdziłem, że przechowywanie obiektu na liście jest nieco szybsze niż użycie wskaźnika. Z drugiej strony wydajność była bardzo zbliżona, więc wszystko sprowadza się do tego, jak zostaną wykorzystane w Twojej aplikacji.
Dla porównania, przy -O3 na moim sprzęcie, wskaźniki zajęły 41 sekund, a surowe obiekty - 30 sekund.
źródło