Mój styl kodowania obejmuje następujący idiom:
class Derived : public Base
{
public :
typedef Base super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
To pozwala mi używać „super” jako aliasu dla Base, na przykład w konstruktorach:
Derived(int i, int j)
: super(i), J(j)
{
}
Lub nawet podczas wywoływania metody z klasy podstawowej w jej przesłoniętej wersji:
void Derived::foo()
{
super::foo() ;
// ... And then, do something else
}
Można go nawet połączyć w łańcuch (wciąż muszę to do tego wykorzystać):
class DerivedDerived : public Derived
{
public :
typedef Derived super; // note that it could be hidden in
// protected/private section, instead
// Etc.
} ;
void DerivedDerived::bar()
{
super::bar() ; // will call Derived::bar
super::super::bar ; // will call Base::bar
// ... And then, do something else
}
W każdym razie uważam, że użycie „typedef super” jest bardzo przydatne, na przykład, gdy Base jest albo gadatliwa i / lub szablonowa.
Faktem jest, że super jest zaimplementowane w Javie, a także w języku C # (gdzie nazywa się to „base”, chyba że się mylę). Ale C ++ nie ma tego słowa kluczowego.
Więc moje pytania:
- czy takie użycie typedef jest bardzo powszechne / rzadkie / nigdy nie widywane w kodzie, z którym pracujesz?
- czy to użycie typedef super Ok (tzn. czy widzisz mocne, czy nie tak mocne powody, aby go nie używać)?
- czy „super” powinno być dobrą rzeczą, czy powinno być nieco wystandaryzowane w C ++, czy też to użycie przez typedef już wystarcza?
Edycja: Roddy wspomniał, że typedef powinien być prywatny. Oznaczałoby to, że żadna klasa pochodna nie byłaby w stanie jej użyć bez ponownego jej opublikowania. Ale sądzę, że zapobiegnie to również super :: super łańcuchowi (ale kto będzie za to płakać?).
Edycja 2: Teraz, kilka miesięcy po masowym użyciu „super”, całym sercem zgadzam się z poglądem Roddy'ego: „super” powinien być prywatny. Chciałbym dwukrotnie głosować za jego odpowiedzią, ale chyba nie mogę.
źródło
super
wyglądaJava
i nie jest niczym złym, ale ... AleC++
obsługuje wielokrotne dziedziczenie.MyFirstBase<MyString, MyStruct<MyData, MyValue>>
template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };
działało to dla mnie z gcc 9.1 --std = c ++ 1y (c ++ 14).Odpowiedzi:
Bjarne Stroustrup wspomina w Design and Evolution of C ++, że
super
jako słowo kluczowe było rozważane przez komitet normalizacyjny ISO C ++ przy pierwszej standaryzacji C ++.Dag Bruck zaproponował to rozszerzenie, nazywając klasę podstawową „odziedziczoną”. We wniosku wspomniano o kwestii wielokrotnego dziedziczenia i oznaczono by niejednoznaczne zastosowania. Nawet Stroustrup był przekonany.
Po dyskusji Dag Bruck (tak, ta sama osoba składająca propozycję) napisał, że propozycja jest możliwa do wdrożenia, technicznie solidna i wolna od poważnych wad oraz że zajmuje się wielokrotnym spadkiem. Z drugiej strony huk nie był wystarczający, a komitet powinien zająć się bardziej dotkliwym problemem.
Michael Tiemann przybył spóźniony, a następnie wykazał, że superfabrykowany maszynopis będzie działał dobrze, stosując tę samą technikę, o którą pytano w tym poście.
Więc nie, to prawdopodobnie nigdy nie zostanie znormalizowane.
Jeśli nie masz kopii, Design and Evolution jest warte ceny ubezpieczenia. Zużyte kopie można kupić za około 10 USD.
źródło
typedef
Technika ma poważną wadę : nie szanuje SUSZENIA. Jedynym sposobem byłoby użycie brzydkich makr do deklarowania klas. Po odziedziczeniu podstawa może być długą, wieloparametrową klasą szablonów lub gorzej. (np. wiele klas) musiałbyś przepisać to wszystko po raz drugi. Wreszcie widzę duży problem z podstawami szablonów, które mają argumenty klas szablonów. W tym przypadku super jest szablonem (a nie instanacją szablonu). Którego nie można wpisać na maszynie. Nawet w C ++ 11 potrzebujeszusing
tego przypadku.Zawsze używałem „odziedziczonego”, a nie super. (Prawdopodobnie ze względu na tło Delphi) i zawsze robię to prywatnym , aby uniknąć problemu, gdy „odziedziczone” zostanie błędnie pominięte w klasie, ale podklasa próbuje go użyć.
Mój standardowy „szablon kodu” do tworzenia nowych klas zawiera typedef, więc nie mam szansy przypadkowo go pominąć.
Nie sądzę, aby przykuta sugestia „super :: super” była dobrym pomysłem. Jeśli to robisz, prawdopodobnie jesteś bardzo przywiązany do określonej hierarchii, a jej zmiana prawdopodobnie źle zepsuje.
źródło
virtual
jak pokazano tutaj: martinbroadhurst.com/typedef-super.htmlJednym z problemów jest to, że jeśli zapomnisz (ponownie) zdefiniować super dla klas pochodnych, wówczas każde wywołanie super :: coś skompiluje się dobrze, ale prawdopodobnie nie wywoła żądanej funkcji.
Na przykład:
(Jak zauważył Martin York w komentarzach do tej odpowiedzi, problem ten można wyeliminować, czyniąc typedef prywatnym, a nie publicznym lub chronionym.)
źródło
super1
dla Base isuper2
Derived? DerivedAgain może korzystać z obu?FWIW Microsoft dodał rozszerzenie dla __super w swoim kompilatorze.
źródło
Super (lub odziedziczona) jest bardzo dobrą rzeczą, ponieważ jeśli chcesz włożyć kolejną warstwę dziedziczenia między Bazową a Pochodną, musisz tylko zmienić dwie rzeczy: 1. „Klasa Podstawowa: foo” i 2. Typedef
Jeśli dobrze pamiętam, Komitet Normalizacyjny C ++ rozważał dodanie słowa kluczowego do tego ... dopóki Michael Tiemann nie zauważył, że ta sztuczka typedef działa.
Jeśli chodzi o wielokrotne dziedziczenie, ponieważ jest pod kontrolą programisty, możesz robić, co chcesz: może super1 i super2 lub cokolwiek innego.
źródło
Właśnie znalazłem alternatywne obejście. Mam duży problem z typedef, który mnie dziś ugryzł:
Więc wymyśliłem lepsze rozwiązanie przy użyciu bardzo prostego szablonu.
Więc teraz zamiast
ty masz
W tym przypadku
BaseAlias
nie jest prywatny i starałem się uchronić przed nieostrożnym użyciem, wybierając nazwę typu, która powinna ostrzegać innych programistów.źródło
public
alias jest wadą, ponieważ jesteś otwarty na błąd wymieniony w odpowiedziach Roddy'ego i Kristophera (np. możesz (przez pomyłkę) wyprowadzićDerived
zamiastMakeAlias<Derived>
)MakeAlias
lub idealnego przekazywania, ale wymaga to odniesienia się doMakeAlias<BaseAlias>
konstruktorów, a nie bezpośrednio doC
konstruktorów.)Nie przypominam sobie tego wcześniej, ale na pierwszy rzut oka to mi się podoba. Jak zauważa Ferruccio , nie działa dobrze w obliczu MI, ale MI jest bardziej wyjątkiem niż regułą i nic nie mówi, że coś musi być przydatne wszędzie, aby było przydatne.
źródło
Widziałem ten idiom stosowany w wielu kodach i jestem prawie pewien, że widziałem go gdzieś w bibliotekach Boost. Jednak, o ile pamiętam, najczęstszą nazwą jest
base
(lubBase
) zamiastsuper
.Ten idiom jest szczególnie użyteczny przy pracy z klasami szablonów. Jako przykład rozważ następującą klasę (z prawdziwego projektu ):
Nie przejmuj się śmiesznymi imionami. Ważną kwestią jest to, że łańcuch dziedziczenia wykorzystuje argumenty typu, aby osiągnąć polimorfizm w czasie kompilacji. Niestety poziom zagnieżdżenia tych szablonów jest dość wysoki. Dlatego skróty są kluczowe dla czytelności i łatwości konserwacji.
źródło
Dość często widziałem, że jest używany, czasami jako super_t, gdy baza jest złożonym typem szablonu (
boost::iterator_adaptor
robi to na przykład)źródło
czy takie użycie typedef jest bardzo powszechne / rzadkie / nigdy nie widywane w kodzie, z którym pracujesz?
Nigdy nie widziałem tego konkretnego wzorca w kodzie C ++, z którym pracuję, ale to nie znaczy, że go nie ma.
czy to użycie typedef super Ok (tzn. czy widzisz mocne, czy nie tak mocne powody, aby go nie używać)?
To nie pozwala na wielokrotne dziedziczenie (w każdym razie czysto).
czy „super” powinno być dobrą rzeczą, czy powinno być nieco wystandaryzowane w C ++, czy też to użycie przez typedef już wystarcza?
Z wyżej wymienionego powodu (wielokrotne dziedziczenie), nie. Powodem, dla którego widzisz słowo „super” w innych wymienionych językach, jest to, że obsługują one tylko pojedyncze dziedziczenie, więc nie ma wątpliwości co do tego, o czym mówi „super”. To prawda, że w tych językach jest to przydatne, ale tak naprawdę nie ma miejsca w modelu danych C ++.
Aha, a FYI: C ++ / CLI obsługuje tę koncepcję w postaci słowa kluczowego „__super”. Pamiętaj jednak, że C ++ / CLI również nie obsługuje wielokrotnego dziedziczenia.
źródło
Jednym z dodatkowych powodów użycia typedef dla nadklasy jest użycie złożonych szablonów w dziedziczeniu obiektu.
Na przykład:
W klasie B byłoby idealnie mieć typedef dla A, w przeciwnym razie utknąłbyś w nim, powtarzając go wszędzie tam, gdzie chciałbyś odwoływać się do członków A.
W takich przypadkach może również działać z wielokrotnym dziedziczeniem, ale nie miałbyś typedefu o nazwie „super”, nazywałby się to „base_A_t” lub coś w tym rodzaju.
--jeffk ++
źródło
Po migracji z Turbo Pascal do C ++ w przeszłości, robiłem to, aby mieć odpowiednik słowa kluczowego „odziedziczonego” Turbo Pascal, który działa w ten sam sposób. Jednak po kilku latach programowania w C ++ przestałem to robić. Odkryłem, że po prostu nie potrzebowałem tego bardzo.
źródło
Nie wiem, czy to rzadkie, czy nie, ale z pewnością zrobiłem to samo.
Jak już wspomniano, trudność w tworzeniu tej części samego języka polega na tym, że klasa korzysta z wielokrotnego dziedziczenia.
źródło
Używam tego od czasu do czasu. Kiedy kilka razy wpisuję typ klasy bazowej, zastąpię go typem podobnym do twojego.
Myślę, że to może być dobry użytek. Jak mówisz, jeśli twoja klasa podstawowa jest szablonem, możesz oszczędzić na pisaniu. Ponadto klasy szablonów mogą przyjmować argumenty, które działają jak zasady dotyczące działania szablonu. Możesz zmienić typ bazy bez konieczności poprawiania wszystkich odniesień do niej, o ile interfejs bazy pozostaje kompatybilny.
Myślę, że użycie przez typedef już wystarczy. Zresztą nie widzę, w jaki sposób zostałby wbudowany w język, ponieważ wielokrotne dziedziczenie oznacza, że może istnieć wiele klas bazowych, więc możesz wpisać je, gdy uznasz, że są odpowiednie dla klasy, która według ciebie jest najważniejszą klasą podstawową.
źródło
Próbowałem rozwiązać ten sam problem; Rzuciłem kilka pomysłów, takich jak używanie szablonów variadic i rozszerzanie paczek, aby umożliwić dowolną liczbę rodziców, ale zdałem sobie sprawę, że zaowocuje to implementacją taką jak „super0” i „super1”. Zniszczyłem go, ponieważ byłoby to ledwo bardziej przydatne niż brak konieczności.
Moje rozwiązanie obejmuje klasę pomocniczą
PrimaryParent
i jest realizowane w następujący sposób:Wtedy dowolna klasa, której chcesz użyć, zostanie zadeklarowana jako taka:
Aby uniknąć konieczności korzystania z wirtualnego dziedziczenia w
PrimaryParent
dniuBaseClass
, konstruktor biorąc zmienną liczbę argumentów jest używany, aby umożliwić budowęBaseClass
.Powodem
public
dziedziczeniaBaseClass
wPrimaryParent
jest umożliwienieMyObject
pełnej kontroli nad spadkiem,BaseClass
pomimo posiadania klasy pomocniczej między nimi.Oznacza to, że każda klasa, którą chcesz mieć,
super
musi korzystać zPrimaryParent
klasy pomocniczej, a każde dziecko może dziedziczyć tylko z jednej klasyPrimaryParent
(stąd nazwa).Kolejnym ograniczeniem dla tej metody jest
MyObject
dziedziczenie tylko jednej klasy, która dziedziczy po niejPrimaryParent
, i którą należy odziedziczyć za pomocąPrimaryParent
. Oto co mam na myśli:Zanim odrzucisz to jako opcję z powodu pozornej liczby ograniczeń i faktu, że pomiędzy każdym dziedzictwem istnieje klasa pośrednia, te rzeczy nie są złe.
Wielokrotne dziedziczenie jest silnym narzędziem, ale w większości przypadków będzie tylko jeden główny rodzic, a jeśli będą inni rodzice, prawdopodobnie będą to klasy Mixin lub klasy, które i tak nie odziedziczą
PrimaryParent
. Jeśli nadal konieczne jest wielokrotne dziedziczenie (chociaż wiele sytuacji korzystałoby z użycia kompozycji do zdefiniowania obiektu zamiast dziedziczenia), to po prostu jawnie zdefiniujsuper
w tej klasie i nie dziedziczPrimaryParent
.Pomysł zdefiniowania
super
w każdej klasie nie jest dla mnie zbyt atrakcyjny,PrimaryParent
ponieważ pozwalasuper
, oczywiście na alias oparty na dziedziczeniu, pozostać w linii definicji klasy zamiast w treści klasy, do której powinny iść dane.To może być tylko ja.
Oczywiście każda sytuacja jest inna, ale weź pod uwagę te rzeczy, które powiedziałem przy podejmowaniu decyzji, której opcji użyć.
źródło
Nie powiem wiele poza obecnym kodem z komentarzami, które pokazują, że super nie oznacza bazy telefonicznej!
super != base.
Krótko mówiąc, co to znaczy „super”? a co to znaczy „baza”?
Te 2 reguły dotyczą klasowych typów maszyn.
Zastanów się nad implementatorem biblioteki i użytkownikiem biblioteki, kto jest super, a kto bazowy?
Aby uzyskać więcej informacji, działający kod do kopiowania wklej do swojego IDE:
Kolejnym ważnym punktem jest tutaj:
wewnątrz
main()
używamy polimorficznych upuszczeń wywołań, które super wywołują w górę, nie są tak naprawdę przydatne w prawdziwym życiu, ale to pokazuje różnicę.źródło
Używam słowa kluczowego __super. Ale jest specyficzne dla Microsoft:
http://msdn.microsoft.com/en-us/library/94dw1w7x.aspx
źródło
Jest to metoda, której używam, która używa makr zamiast typedef. Wiem, że nie jest to sposób wykonywania zadań w C ++, ale może być wygodny, gdy łączy się iteratory ze sobą poprzez dziedziczenie, gdy tylko dziedziczona klasa bazowa najdalej w dół hierarchii działa na odziedziczone przesunięcie.
Na przykład:
Ogólna konfiguracja, której używam, bardzo ułatwia odczytywanie i kopiowanie / wklejanie między drzewem dziedziczenia, które ma zduplikowany kod, ale musi zostać zastąpione, ponieważ typ zwracany musi pasować do bieżącej klasy.
Można użyć małych liter,
super
aby odtworzyć zachowanie widoczne w Javie, ale moim stylem kodowania jest używanie wszystkich wielkich liter w makrach.źródło