2013-08-27 19 views
5

To raczej nie jest pytanie "jak to zrobić" to raczej "jak to zrobić we właściwy sposób"C++ 11 non-owning reference/pointer do unique_ptr?

Zajmuję się edytowaniem w Qt, gdzie różne widgety wyświetlają dzieci i jego zmienne (członkowskie). Każdy z tych widgetów powinien zawierać odniesienie/wskaźnik do edytowanego potomka, aby wyświetlić i zmienić jego zmienne składowe.

Pierwsza próba była starą metodą ANSI C, której nauczyłem się (i nadal trochę utknąłem) za pomocą prostego wskaźnika surowego do używanych obiektów. Działa dobrze, ale ponieważ standard C++ 11 obsługuje inteligentny wskaźnik, a ich używanie jest zalecane, staram się z nich korzystać.

Problem polega na tym, że nie jestem pewien, co to jest „najlepszy sposób”, aby je zastosować w tym przypadku ... Po przeczytaniu Smart Pointers: Or who owns you baby? i Which kind of pointer do I use when? i kilka innych doszedłem do innych wniosków:

najpierw należy użyć *unique_ptr, ponieważ edytowany obiekt jest wyraźnie właścicielem, który tworzy i usuwa jego potomka. Widżety odnoszą się po prostu do dziecka, aby je pokazać lub zmienić. Problemem jest to, w jaki sposób należy widgety odnoszą się do dziecka ...

teraz jestem po prostu nadal używa surowego wskaźnik dostałem ze sposobu unique_ptrget() ale to wydaje się trochę wadliwa do mnie. Nadal mogę przypadkowo usunąć połączenie na wskaźniku i anulować korzyści z inteligentnego wskaźnika.

Drugim podejściem jest użycie shared_ptr, ponieważ wiele obiektów odnosi się do dziecka i edytuje je. Również przypadkowe usunięcie go w jednym widżecie nie zaszkodzi, ponieważ nadal jest własnością innych obiektów. Problem polega na tym, że są jego właścicielami. Kiedy chcę usunąć go z edytowanego obiektu, muszę również zasygnalizować wszystkie widżety, aby je skasować, zanim naprawdę zniknie. (to znowu wydaje się wadliwe i podatne na błędy)

Nie jestem zadowolony z obu sposobów. Czy istnieje czysty (er) sposób wskazywania potomka obiektu obiektu unique_ptr? Czy może brakuje mi zupełnie innego i lepszego podejścia do tego problemu?

+0

Czy potrzebujesz obiektów referencyjnych do ostrzeżenia, gdy przywoływany obiekt jest zniszczony? –

+2

W tego rodzaju sytuacjach naprawdę potrzebujemy [głupich inteligentnych wskaźników] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3514.pdf). –

+0

@BenjaminLindley Na razie muszą zostać powiadomione, aby usunąć odpowiednie widżety/kontrolki i nie używać ich po usunięciu. na przykład jeden widget potrzebuje uzyskać współrzędne jednego potomka, aby umieścić je w widoku renderowania OpenGL. Ale naprawdę staram się tego uniknąć, ponieważ każdy widget musi reagować na emitowany sygnał. Byłoby bardziej idealne, gdyby właściciel mógł sam określić, czy obiekt nadal istnieje, czy nie. – nils277

Odpowiedz

2

Chcesz użyć urządzenia shared_ptr zamiast swoich unique_ptr i weak_ptr zamiast surowych wskaźników. To da dokładnie to, czego szukasz. Obiekty weak_ptr nie będą kolidować ze zdolnością edytowanego obiektu do usuwania bazowego obiektu.

+0

Dzięki, wygląda na to, czego dokładnie szukałem. – nils277

1

Twój przypadek użycia nie przekłada się bezpośrednio na wymagania (co, jeśli ktoś inny usunie widżet podczas edycji?), Ale zakładam, że nie potrzebujesz niczego poza gołym wskaźnikiem.

Biblioteka standardowa nie zapewnia żadnej ścisłej klasy obserwatora-obserwatora.Wśród jednostek obserwacyjnych:

  • Nullable, modyfikowalnych wskaźników rodzimej typu (T *)
  • dla pustych, nie Zmienne odniesień do natywnego typu (T &)
  • dla pustych, odniesienia Zmienne/prokurentów klasie Typ (std::reference_wrapper<T>)
  • pustych, poczucie potwierdzających, zmienne wskaźniki do obiektów zarządzanych (std::weak_ptr<T>)

jeśli chcesz nie podlegający zerowaniu, zmienny wskaźnik na niezarządzany obiekt, co jest dość rozsądną rzeczą, jakiej można chcieć, można przetoczyć własną.

Ale nagie wskazówki nie są takie złe. Jedyną różnicą jest to, że może to być nullptr i że nie ma ładnej, długiej, jawnej nazwy wewnątrz przestrzeni nazw std.

1

Jeśli używasz Qt, można rozważyć użycie Qt inteligentne kursory zamiast std :: inteligentne wskazówek:

lub do QObjects:

  • QPointer (lub QWeakPointer, szczególnie w Qt4)

lub do osiągnięcia wymiany kopiowanie przy zapisie danych, QT pojemnik i styl QString:

Istnieją inne klasy wskaźnik zbyt, ale niektóre z powyższych najprawdopodobniej rób co chcesz. Również ważne, jeśli masz dane w klasach kontenerowych Qt, QStrings lub innych, obsługują one własną pamięć z semantyką "copy-on-write" i generalnie powinny być przekazywane jako zwykłe wartości (czasami jako referencje) zamiast wskaźników.

Ale co najważniejsze, nie używaj std::unique_ptr lub std::shared_ptr z QObjects które mają rodzice, bo jeśli rodzic usuwa dziecko, potem std :: wskaźnik będzie go usunąć znowu upaść program (inny sposób będzie działać OK, dziecko powiadomi rodzica, że ​​zostało usunięte). Innymi słowy, istnieje duża szansa na subtelne błędy, jeśli miksujesz QObjects i std :: pointers, więc po prostu tego nie rób. Z twojego pytania nie wynika jasno, czy robisz to, mówiąc na wszelki wypadek.

+0

Masz dobry punkt. Nie oczekiwałem, że 'QObject' będzie miał problem ze std :: point. Myślę, że 'QSharedPointer' i' QWeakPointer' są dla mnie drogą, ponieważ moje obiekty pochodzą z QObject, aby korzystać z sygnałów i gniazd. – nils277

+0

@ nill277 Zauważ, że ten sam problem występuje w 'QSharedPointer', nie ma specjalnego wsparcia dla' QObject' usuniętego przez obiekt nadrzędny. Jeśli nie ma obiektu nadrzędnego, to jest w porządku, ale poza tym to samo dotyczy. Domyślam się, że jest to dobry sposób na zrobienie tego, jeśli twoje QObjects nie mają logicznego rodzica/właściciela, po prostu bądź ostrożny, pamiętasz, aby zachować go w ten sposób (np. Nie masz konstruktora, który pozwala dawać rodzicowi). Jeśli mają one rodzica, to po prostu użyj 'QPointer' do przechowywania słabych odniesień gdzie indziej. – hyde

0

Istnieje propozycja dla world's dumbest smart pointer, która jest wskaźnikiem nie będącym właścicielem. Zasadniczo jest to T*, które mówi "oh, a przy okazji, nie roszczenia własności nad tymi danymi".

W tym przypadku musisz zarządzać, że wskaźnik obserwacji/pasywny/niemy zostanie zresetowany, jeśli zniknie unique_ptr - połowa ręcznego zarządzania cyklem życia. Używanie surowego T* o nazwie wskazującej, że nie jest właścicielem, działa tak samo dobrze, jak wcześniej.

Są pewne korzyści z powyższego, zwłaszcza gdy masz obiekty z zależnym życiem.

Jeśli nie, to działa shared_ptr i weak_ptr. Należy jednak pamiętać, że każdy z użytkownikiem weak_ptr może utworzyć nowy shared_ptr, który może wydłużyć czas działania obiektu wspólnego poza oryginalnym shared_ptr. To musi się zdarzyć, ponieważ w innym przypadku występuje sytuacja wyścigowa, w której użytkownik urządzenia weak_ptr zapewnia poprawność danych, a następnie używa go, ale zanim to zrobi, dane zostaną zniszczone.

Tak więc weak_ptr musi albo być w stanie zapobiec zniszczeniu shared_ptr (zablokowanie w wielowątkowym kontekście, powodując "awarię" w jakiś sposób w kontekstach jednowątkowych). "Awaria" w tym przypadku polega na wydłużeniu czasu życia, co rozwiązuje zarówno problem wielowątkowy, jak i problem jednowątkowy.

(Jednolity problemem gwintowany jest, aby sprawdzić, czy weak_ptr jest dobre, zrobić coś, co powoduje shared_ptr zresetować, a następnie nadal chcą korzystać z danych weak_ptr „s bez ponownego sprawdzenia. Naiwnie, może to być unikane z wyjątkami ale po bliższej inspekcji napotkasz problemy, w których będziesz musiał zakodować wszystkie metody sprawdzania usunięcia przed uzyskaniem dostępu do this i wyrzucić, jeśli usunięto this, dość surowe wymaganie!)