2011-12-05 11 views
6

Zwykle używam boost :: scoped_ptr dla pimpl (na jednego powodu, bo wtedy nie dostanę niespodzianek, jeżeli zapomnę do czynienia z konstruktora kopii)pimpl-idiom w szablonie; który inteligentny wskaźnik?

z szablonami jednak nie można po prostu umieścić w destruktor plik cpp, w którym imp jest w pełni zdefiniowany, aby spełnić wymagania destruktora scoped_ptr. I tak to działa, ale nie jestem pewien, czy można go zagwarantować, czy tylko przypadkiem. Czy istnieje "najlepsza praktyka" lub standard? Czy scoped_ptr jest najlepszym inteligentnym wskaźnikiem dla pimplów w klasach, które nie mogą być kopiowane?

template <class T> class C { 
public: 
    C(){} 
    ~C(){} 
private: 
    boost::scoped_ptr<T> pimpl_; 
}; 
+4

Ten rodzaj implementacji PIMPL nie ma sensu, ponieważ aby utworzyć instancję szablonu C, trzeba wiedzieć o typie T. PIMPL, z drugiej strony, całkowicie ukrywa ekwiwalent T od użytkownika. –

+0

@ VladLazarenko Hmm, myślałem, że boost :: scoped_ptr działa również na wcześniej zadeklarowanych klasach. W tym przypadku zależy to od tego, czy T jest zdefiniowane czy przedszkolne. Instancja tej scoped_ptr byłaby ukryta w implementacji (pimpl_ (nowy T()). –

+0

@DavidFeurle: Niezupełnie, aby ten szablon działał, rozmiar 'T', jak również jego interfejs, musi być odsłonięty, ponieważ "klienci" muszą utworzyć instancję szablonu, na przykład, gdzie nazywacie 'new T()'? Nie można tego ukryć w pliku "cpp", ponieważ musi on być w szablonie, więc to nie jest PIMPL. –

Odpowiedz

1

boost::shared_ptr nie wymaga kompletnej definicji inne niż w jej punktem w instancji — konstruktor, w przypadku pimpl. boost::shared_ptr jest nie odpowiednie dla idiomu pimpl, jednak, ponieważ daje bardzo nieoczekiwaną semantykę (semantyka odniesienia dla przypisania lub kopiowania); jeśli naprawdę chcesz dodać większą złożoność inteligentnego wskaźnika , to będzie bardziej odpowiedni (ale to wymaga pełnej definicji w punkcie, w którym jego destruktor jest utworzony jako ).

Jeśli chodzi o szablony, nie ma sensu używać idiomu pimpl dla szczegółów implementacji z nagłówka. W absencji export, wszystkie szczegóły implementacji szablonu klasy muszą być dołączone wszędzie tam, gdzie szablon jest używany, więc motywacja idiomu pimpl przestaje istnieć.

13

Zdarza się, że Herb Sutter ponownie zaczął pisać swoje GotWs po długim czasie. Jeden z pierwszych nowych jest związany z "Zaporami kompilacyjnymi".

Czasami warto spojrzeć na:

GotW #100: Compilation Firewalls (Difficulty: 6/10)

i

GotW #101: Compilation Firewalls, Part 2 (Difficulty: 8/10)

+0

Fajne Uwielbiam jego książki.Najlepiej odpowiedziałem na moje pytanie o jaki typ wskaźnika użyć za pomocą unique_ptr. – odinthenerd

+0

tworząc odpowiedź: ' unique_ptr pimpl' – sehe

+1

Wygląda na to, że Herb kupiony w bardziej złożony sposób jest lepszą filozofią. Wszystkie jego rozwiązania oparte na standardach wprowadzają dodatkową złożoność, bez żadnego realnego zysku. –

2

Dwa lata później rozumiem sytuację znacznie lepiej, w interesie utrzymania odpowiedzi przepełnienia stosu istotne i aktualne tutaj jest, jak bym odpowiedział na pytanie dzisiaj.

Przesłanka mojego pierwotnego pytania jest nieco wadliwa. Powodem użycia pimpl-idiom jest ukrywanie szczegółów implementacji z kompilatora. Odbywa się to poprzez przechowywanie implementacji za pomocą nieprzezroczystego wskaźnika (wskaźnik do zadeklarowanego, ale nie zdefiniowanego typu danych). Może to znacznie zmniejszyć ilość nagłówków potrzebnych dla innych jednostek kompilacji, które wchodzą w interakcje z klasą, a tym samym przyspieszają czas kompilacji. W przypadku szablonu w moim pytaniu wymagane jest, aby typ T był w pełni znany w momencie tworzenia, co w praktyce wymaga pełnego zdefiniowania typu imp, gdziekolwiek jest C<ImplType>, co wyraźnie nie jest przykładem pimpl-idiomu w klasycznym tego słowa znaczeniu.

Istnieją inne powody, dla których warto przechowywać dane o klasach za pomocą prywatnego wskaźnika, na przykład pozwala to na łatwą implementację ruchu i zamiany bez rzucania, a także jest dobre, jeśli klasa musi spełnić silną gwarancję wyjątku (zobacz: zamiana idiomu What is the copy-and-swap idiom?). Z drugiej strony dodaje warstwę pośrednią (często skutkującą brakami w pamięci podręcznej) przy każdym dostępie do impl i przydziału/dealokacji sterty po utworzeniu i zniszczeniu imp.Mogą to być znaczne kary za skuteczność, dlatego nie należy uważać tego rozwiązania za srebrną kulę.

Jeśli możesz użyć C++ 11, należy użyć std :: unique_ptr zamiast boost :: scoped_ptr.

Powiązane problemy