Czy ktoś może wskazać mi jakieś fajne zasoby do zrozumienia i korzystania z zagnieżdżonych klas? Mam trochę materiałów, takich jak zasady programowania i podobne rzeczy Centrum wiedzy IBM - Zagnieżdżone klasy
Ale wciąż mam problem ze zrozumieniem ich celu. Czy ktoś mógłby mi pomóc?
c++
nested
inner-classes
w okularach
źródło
źródło
typedef
. 3. ponieważ dodają dodatkowy poziom wcięcia w środowisku, w którym unikanie długich linii jest już trudne 4. ponieważ deklarujesz dwa koncepcyjnie oddzielne obiekty w jednejclass
deklaracji itp.Odpowiedzi:
Zagnieżdżone klasy świetnie nadają się do ukrywania szczegółów implementacji.
Lista:
Tutaj nie chcę ujawniać Węzła, ponieważ inne osoby mogą zdecydować się na użycie klasy, co utrudniłoby mi aktualizację mojej klasy, ponieważ wszystko, co jest widoczne, jest częścią publicznego interfejsu API i musi zostać utrzymane zawsze . Ustawiając klasę jako prywatną, nie tylko ukrywam implementację, ale także mówię, że jest moja i mogę ją zmienić w dowolnym momencie, abyście nie mogli jej użyć.
Spójrz,
std::list
czystd::map
wszystkie zawierają ukryte klasy (czy też?). Chodzi o to, że mogą, ale nie muszą, ale ponieważ implementacja jest prywatna i ukryta, twórcy STL byli w stanie zaktualizować kod bez wpływu na sposób jego użycia lub pozostawienia dużej ilości starego bagażu leżącego wokół STL, ponieważ potrzebują aby zachować kompatybilność wsteczną z jakimś głupcem, który zdecydował, że chce użyć ukrytej w nim klasy Nodelist
.źródło
Node
nie powinno być w ogóle widoczne w pliku nagłówkowym.detail
konwencję: Zamiast tego w zależności od takich konwencji należy pamiętać o sobie, lepiej polegać na kompilatorze, który śledzi je za Ciebie.Klasy zagnieżdżone są jak zwykłe klasy, ale:
Kilka przykładów:
Publicznie zagnieżdżaj klasę, aby umieścić ją w zakresie odpowiedniej klasy
Załóżmy, że chcesz mieć klasę,
SomeSpecificCollection
która agregowałaby obiekty klasyElement
. Następnie możesz:zadeklaruj dwie klasy:
SomeSpecificCollection
iElement
- złe, ponieważ nazwa „Element” jest wystarczająco ogólna, aby spowodować ewentualne zderzenie nazwywprowadzić przestrzeń nazw
someSpecificCollection
i zadeklarować klasysomeSpecificCollection::Collection
isomeSpecificCollection::Element
. Nie ma ryzyka kolizji nazwisk, ale czy może stać się bardziej gadatliwy?deklarują dwie globalne klasy
SomeSpecificCollection
iSomeSpecificCollectionElement
- co ma drobne wady, ale prawdopodobnie jest OK.deklaruj klasę globalną
SomeSpecificCollection
i klasęElement
jako klasę zagnieżdżoną. Następnie:SomeSpecificCollection
odwołujesz się do sprawiedliwegoElement
i wszędzie innego jakoSomeSpecificCollection::Element
- który wygląda + - tak samo jak 3., ale bardziej wyraźnieSomeSpecificCollection
to także klasa.Moim zdaniem ostatni wariant jest zdecydowanie najbardziej intuicyjny, a zatem najlepszy.
Pozwólcie, że podkreślę - nie jest to duża różnica w tworzeniu dwóch globalnych klas o bardziej pełnych nazwach. To tylko drobny szczegół, ale imho sprawia, że kod jest bardziej przejrzysty.
Wprowadzenie innego zakresu w zakresie klasy
Jest to szczególnie przydatne do wprowadzania typedefs lub enum. Po prostu opublikuję tutaj przykład kodu:
Jeden wtedy zadzwoni:
Ale patrząc na propozycje uzupełnienia kodu
Product::
, często pojawia się lista wszystkich możliwych wartości wyliczenia (BOX, FANCY, CRATE) i łatwo jest tutaj popełnić błąd (wyliczone w C ++ 0x wyliczenia tego rodzaju rozwiązują to, ale nieważne ).Ale jeśli wprowadzisz dodatkowy zakres dla tych wyliczeń za pomocą klas zagnieżdżonych, rzeczy mogą wyglądać następująco:
Następnie połączenie wygląda następująco:
Następnie pisząc
Product::ProductType::
IDE, otrzymasz tylko wyliczenia z sugerowanego pożądanego zakresu. Zmniejsza to również ryzyko popełnienia błędu.Oczywiście może to nie być potrzebne w przypadku małych klas, ale jeśli ktoś ma dużo wyliczeń, ułatwia to programistom klienckim.
W ten sam sposób możesz „zorganizować” dużą grupę pism maszynowych w szablonie, jeśli kiedykolwiek zajdzie taka potrzeba. Czasami jest to użyteczny wzór.
Idiom PIMPL
PIMPL (skrót od Pointer to IMPLementation) to idiom przydatny do usuwania szczegółów implementacji klasy z nagłówka. Zmniejsza to potrzebę ponownej kompilacji klas w zależności od nagłówka klasy, ilekroć zmienia się część „implementacyjna” nagłówka.
Zwykle jest implementowany za pomocą zagnieżdżonej klasy:
Xh:
X.cpp:
Jest to szczególnie przydatne, jeśli pełna definicja klasy wymaga definicji typów z biblioteki zewnętrznej, która ma ciężki lub po prostu brzydki plik nagłówka (weź WinAPI). Jeśli używasz PIMPL, możesz zawrzeć dowolną funkcjonalność specyficzną dla WinAPI tylko w
.cpp
i nigdy go nie włączać.h
.źródło
struct Impl; std::auto_ptr<Impl> impl;
Ten błąd spopularyzował Herb Sutter. Nie używaj auto_ptr na niekompletnych typach, lub przynajmniej podejmij środki ostrożności, aby uniknąć generowania błędnego kodu.auto_ptr
niepełny typ w większości implementacji, ale technicznie jest to UB w przeciwieństwie do niektórych szablonów w C ++ 0x (np.unique_ptr
), Gdzie wyraźnie zaznaczono, że parametr szablonu może być niekompletny typ i gdzie dokładnie ten typ musi być kompletny. (np. użycie~unique_ptr
)T
odunique_ptr
może być niekompletna typu”.enum class
.Nie używam klas zagnieżdżonych, ale używam ich od czasu do czasu. Zwłaszcza gdy definiuję jakiś typ danych, a następnie chcę zdefiniować funktor STL zaprojektowany dla tego typu danych.
Rozważmy na przykład ogólną
Field
klasę, która ma numer identyfikacyjny, kod typu i nazwę pola. Jeśli chcę wyszukać jedenvector
z nichField
według numeru ID lub nazwy, mógłbym zbudować funktor, aby to zrobić:Następnie kod, który musi wyszukać te,
Field
może użyćmatch
zakresu wField
samej klasie:źródło
Można zaimplementować wzorzec Konstruktora z klasą zagnieżdżoną . Zwłaszcza w C ++ osobiście uważam, że jest semantycznie czystszy. Na przykład:
Zamiast:
źródło