W kilku artykułach przeczytałem, że surowe wskaźniki prawie nigdy nie powinny być używane. Zamiast tego powinny być zawsze opakowane w inteligentne wskaźniki, niezależnie od tego, czy są to wskaźniki zakresowe, czy wspólne.
Zauważyłem jednak, że frameworki takie jak Qt, wxWidgets i biblioteki takie jak Boost nigdy nie zwracają ani nie oczekują inteligentnych wskaźników, jakby w ogóle ich nie używały. Zamiast tego zwracają lub oczekują surowych wskazówek. Czy jest jakiś powód? Czy powinienem trzymać się z daleka od inteligentnych wskaźników, kiedy piszę publiczny interfejs API, i dlaczego?
Zastanawiam się tylko, dlaczego inteligentne wskaźniki są zalecane, gdy wiele dużych projektów wydaje się ich unikać.
c++
api
pointers
smart-pointers
laurent
źródło
źródło
unique_ptr
? Absolutnie nic. Czy Qt / WxWidgets są przeznaczone dla systemów wbudowanych czy systemów czasu rzeczywistego? Nie, są przeznaczone maksymalnie dla systemów Windows / Mac / Unix na komputerach stacjonarnych. Inteligentne wskazówki są dla programistów, którzy chcą to poprawnie.Odpowiedzi:
Pomijając fakt, że wiele bibliotek zostało napisanych przed pojawieniem się standardowych inteligentnych wskaźników, największym powodem jest prawdopodobnie brak standardowego C ++ Application Binary Interface (ABI).
Jeśli piszesz bibliotekę tylko z nagłówkiem, możesz przekazać inteligentne wskaźniki i standardowe pojemniki do treści swojego serca. Ich źródło jest dostępne dla Twojej biblioteki w czasie kompilacji, więc polegasz tylko na stabilności ich interfejsów, a nie ich implementacji.
Jednak ze względu na brak standardowego interfejsu ABI na ogół nie można bezpiecznie przekazywać tych obiektów przez granice modułów. GCC
shared_ptr
prawdopodobnie różni się od MSVCshared_ptr
, który również może różnić się od Intelshared_ptr
. Nawet z tym samym kompilatorem nie ma gwarancji, że te klasy będą binarnie zgodne między wersjami.Najważniejsze jest to, że jeśli chcesz rozprowadzać wstępnie skompilowaną wersję swojej biblioteki, potrzebujesz standardowego ABI, na którym możesz polegać. C go nie ma, ale dostawcy kompilatorów bardzo dobrze oceniają interoperacyjność między bibliotekami C dla danej platformy - istnieją de facto standardy.
Sytuacja nie jest tak dobra dla C ++. Poszczególne kompilatory mogą obsługiwać współdziałanie między swoimi własnymi plikami binarnymi, więc masz możliwość dystrybucji wersji dla każdego obsługiwanego kompilatora, często GCC i MSVC. Ale w świetle tego większość bibliotek po prostu eksportuje interfejs C - a to oznacza surowe wskaźniki.
Kod spoza biblioteki powinien jednak generalnie preferować inteligentne wskaźniki od surowego.
źródło
Przyczyn może być wiele. Aby wymienić kilka z nich:
Edycja : korzystanie z inteligentnych wskaźników jest całkowicie wyborem programisty. To zależy od różnych czynników.
W systemach o krytycznym znaczeniu dla wydajności możesz nie chcieć używać inteligentnych wskaźników, które generują narzut
Projekt, który wymaga kompatybilności wstecznej, możesz nie chcieć używać inteligentnych wskaźników, które mają specyficzne funkcje C ++ 11
Edit2 Istnieje ciąg kilku głosów przeciw w ciągu 24 godzin z powodu poniższego fragmentu. Nie rozumiem, dlaczego odpowiedź jest odrzucana, mimo że poniżej jest tylko sugestią dodatku, a nie odpowiedzią.
Jednak C ++ zawsze ułatwia otwieranie opcji. :) np
A w swoim kodzie użyj go jako:
Dla tych, którzy twierdzą, że inteligentny wskaźnik i surowy wskaźnik są różne, zgadzam się z tym. Powyższy kod był tylko pomysłem, w którym można napisać kod, który jest wymienny tylko z a
#define
, to nie jest przymus ;Na przykład
T*
musi zostać wyraźnie usunięty, ale inteligentny wskaźnik nie. Możemy mieć szablon,Destroy()
który to obsłuży.i użyj go jako:
W ten sam sposób dla surowego wskaźnika możemy go skopiować bezpośrednio, a dla inteligentnego wskaźnika możemy użyć specjalnej operacji.
Gdzie
Assign()
jest jak:źródło
std::auto_ptr
to , że jest częścią standardu przez długi czas (i zauważ, że lubięstd::auto_ptr
jako typ zwracany dla funkcji tworzących obiekty, nawet jeśli tak jest prawie bezużyteczne wszędzie indziej). W C ++ 11std::unique_ptr
nie ma dodatkowych kosztów w stosunku do zwykłego wskaźnika.unique_ptr
i znikaniaauto_ptr
kodu, który jest przeznaczony dla C ++ 03 powinien używać tego później, podczas gdy kod ukierunkowany na C ++ 11 może używać tego pierwszego. Inteligentne wskazówki nie sąshared_ptr
, istnieje wiele standardów i nie ma ich żadnego, w tym propozycje do standardu, które zostały odrzucone jakomanaged_ptr
unique_ptr
kosztów wykonania,unique_ptr
są zdecydowanie najczęściej używane. Próbka Kod podać również mylące, bounique_ptr
iT*
są zupełnie różne pojęcia. Fakt, że odnosisz się do nich obu jako,type
sprawia wrażenie, że można je zamienić na siebie.Istnieją dwa problemy z inteligentnymi wskaźnikami (przed C ++ 11):
Domyślny inteligentne wskaźnik, tym, że jest wolne od kosztów, jest
unique_ptr
. Niestety wymaga semantyki przenoszenia w C ++ 11, która pojawiła się dopiero niedawno. Wszystkie inne inteligentne wskaźniki mają koszt (shared_ptr
,intrusive_ptr
) lub mają mniej niż idealną semantykę (auto_ptr
).Z C ++ 11 tuż za rogiem, przynoszącym
std::unique_ptr
, można by pomyśleć, że to już koniec ... Nie jestem taki optymistyczny.Tylko kilka głównych kompilatorów implementuje większość C ++ 11 i tylko w ich najnowszych wersjach. Możemy spodziewać się, że duże biblioteki, takie jak QT i Boost, będą przez jakiś czas skłonne zachować zgodność z C ++ 03, co w pewnym stopniu wyklucza szerokie zastosowanie nowych i błyszczących inteligentnych wskaźników.
źródło
Nie powinieneś trzymać się z daleka od inteligentnych wskaźników, mają one swoje zastosowanie zwłaszcza w aplikacjach, w których musisz przesuwać obiekt.
Biblioteki mają tendencję do zwracania wartości lub wypełniania obiektu. Zwykle nie mają obiektów, których trzeba używać w wielu miejscach, więc nie ma potrzeby, aby używali inteligentnych wskaźników (przynajmniej nie w ich interfejsie, mogą ich używać wewnętrznie).
Mogę na przykład wziąć pod uwagę bibliotekę, nad którą pracowaliśmy, w której po kilku miesiącach rozwoju zdałem sobie sprawę, że używaliśmy wskaźników i inteligentnych wskaźników tylko w kilku klasach (3-5% wszystkich zajęć).
Przekazywanie zmiennych przez odniesienie było wystarczające w większości miejsc, używaliśmy inteligentnych wskaźników, gdy mieliśmy obiekt, który mógł być zerowy, i surowych wskaźników, gdy zmuszała nas do tego biblioteka, której używaliśmy.
Edytuj (nie mogę komentować z powodu mojej reputacji): przekazywanie zmiennych przez odniesienie jest bardzo elastyczne: jeśli chcesz, aby obiekt był tylko do odczytu, możesz użyć odwołania do const (nadal możesz wykonać kilka nieprzyjemnych rzutów, aby móc napisać obiekt ), ale uzyskujesz maksymalną możliwą ochronę (to samo dotyczy inteligentnych wskaźników). Ale zgadzam się, że o wiele przyjemniej jest po prostu zwrócić obiekt.
źródło
Qt bezcelowo wymyślał na nowo wiele części biblioteki Standard, próbując stać się Javą. Uważam, że obecnie ma swoje własne inteligentne wskazówki, ale ogólnie nie jest to szczyt projektu. wxWidgets, o ile wiem, został zaprojektowany na długo przed napisaniem użytecznych inteligentnych wskaźników.
Jeśli chodzi o Boost, w pełni oczekuję, że będą używać inteligentnych wskaźników wszędzie tam, gdzie jest to właściwe. Być może będziesz musiał być bardziej szczegółowy.
Ponadto nie zapominaj, że istnieją inteligentne wskaźniki wymuszające prawo własności. Jeśli interfejs API nie ma semantyki własności, to po co używać inteligentnego wskaźnika?
źródło
QString
, wxWidgets mawxString
, MFC ma strasznie nazwaneCString
. Czy UTF-8std::string
nie jest wystarczająco dobry dla 99% zadań GUI?Dobre pytanie. Nie znam konkretnych artykułów, do których się odnosisz, ale od czasu do czasu czytałem podobne rzeczy. Podejrzewam, że autorzy takich artykułów skłaniają się ku programowaniu w stylu C ++. Jeśli pisarz programuje w C ++ tylko wtedy, gdy musi, a potem wraca do Javy lub tak szybko, jak to możliwe, to tak naprawdę nie podziela sposobu myślenia C ++.
Można podejrzewać, że niektórzy lub większość tych samych autorów preferuje menedżery pamięci zbierające elementy bezużyteczne. Nie, ale myślę inaczej niż oni.
Inteligentne wskaźniki są świetne, ale muszą zachować liczbę referencji. Utrzymanie liczby referencyjnej wiąże się z kosztami - często niewielkimi kosztami, ale mimo to - w czasie wykonywania. Nie ma nic złego w oszczędzaniu tych kosztów przy użyciu samych wskaźników, zwłaszcza jeśli wskaźniki są zarządzane przez destruktory.
Jedną z doskonałych cech C ++ jest wsparcie dla programowania systemów wbudowanych. Częścią tego jest użycie samych wskaźników.
Aktualizacja: komentator poprawnie zauważył, że nowy C ++
unique_ptr
(dostępny od TR1) nie liczy referencji. Komentator ma też inną definicję „inteligentnego wskaźnika” niż ja mam na myśli. Może mieć rację co do definicji.Dalsza aktualizacja: Wątek komentarzy poniżej świeci. Wszystko to jest zalecane do przeczytania.
źródło
shared_ptr
zachowuje liczbę referencji. Istnieje wiele innych inteligentnych typów wskaźników, które w ogóle nie przechowują liczby odwołań. Wreszcie wspomniane biblioteki są ukierunkowane na platformy, które mają wiele zasobów do stracenia. Nie to, że byłem zwolennikiem krytyki, ale mówię tylko, że Twój post jest pełen błędów.shared_ptr
nie ma kosztów ogólnych. Ma narzut tylko wtedy, gdy nie potrzebujesz bezpiecznej wątkowo semantyki współdzielonej własności, która jest tym, co zapewnia.Istnieją również inne rodzaje inteligentnych wskaźników. Możesz chcieć wyspecjalizowanego inteligentnego wskaźnika do czegoś takiego jak replikacja sieciowa (takiego, który wykrywa, czy jest dostępny i wysyła wszelkie modyfikacje do serwera lub czegoś podobnego), przechowuje historię zmian, zaznacza fakt, że uzyskano do niego dostęp, aby można było go zbadać, kiedy zapisujesz dane na dysku i tak dalej. Nie jestem pewien, czy zrobienie tego we wskaźniku jest najlepszym rozwiązaniem, ale używanie wbudowanych inteligentnych typów wskaźników w bibliotekach może spowodować zamknięcie ludzi w nich i utratę elastyczności.
Ludzie mogą mieć wiele różnych wymagań i rozwiązań w zakresie zarządzania pamięcią poza inteligentnymi wskaźnikami. Może chciałbym sam zarządzać pamięcią, mógłbym przydzielić miejsce na rzeczy w puli pamięci, więc jest przydzielane z góry, a nie w czasie wykonywania (przydatne w grach). Być może używam implementacji C ++ ze zbieraniem śmieci (C ++ 11 umożliwia to, chociaż jeszcze nie istnieje). A może po prostu nie robię nic na tyle zaawansowanego, żeby martwić się, że zawracam im głowę, wiem, że nie zapomnę o niezainicjowanych obiektach i tak dalej. Może po prostu jestem pewien swojej zdolności do zarządzania pamięcią bez kuli wskazującej.
Integracja z C to także inny problem.
Inną kwestią jest to, że inteligentne wskaźniki są częścią STL. C ++ jest przeznaczony do użytku bez STL.
źródło
Zależy to również od domeny, w której pracujesz. Zarabiam na życie pisaniem silników gier, unikamy boostów jak zarazy, w grach narzuty związane z doładowaniem są nie do przyjęcia. W naszym głównym silniku napisaliśmy naszą własną wersję stl (podobnie jak ea stl).
Gdybym miał napisać aplikację do formularzy, mógłbym rozważyć użycie inteligentnych wskaźników; ale kiedy zarządzanie pamięcią jest drugą naturą, brak szczegółowej kontroli nad pamięcią staje się cicho irytujący.
źródło