Używanie „super” w C ++

203

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ę.

paercebal
źródło
Niesamowite! Dokładnie tego szukałem. Nie sądzę, że kiedykolwiek potrzebowałem tej techniki. Doskonałe rozwiązanie dla mojego kodu międzyplatformowego.
AlanKley
6
Dla mnie superwygląda Javai nie jest niczym złym, ale ... Ale C++obsługuje wielokrotne dziedziczenie.
ST3,
2
@ user2623967: Racja. W przypadku zwykłego dziedziczenia wystarczy jedno „super”. Teraz, jeśli masz wiele elementów dziedziczenia, dobrym rozwiązaniem jest posiadanie „superA”, „superB” itp.: CHCESZ wywołać metodę z jednej lub innej implementacji, więc musisz powiedzieć, jakiej implementacji chcesz. Użycie „super” podobnego do typedef umożliwia MyFirstBase<MyString, MyStruct<MyData, MyValue>>
podanie
Pamiętaj, że podczas dziedziczenia po szablonie nie trzeba dołączać argumentów szablonu podczas odwoływania się do niego. Na przykład: 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).
Thanasis Papoutsidakis
1
... Um, korekta. Wygląda na to, że działa w dowolnym standardzie c ++, nie tylko 14.
Thanasis Papoutsidakis

Odpowiedzi:

151

Bjarne Stroustrup wspomina w Design and Evolution of C ++, że superjako 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.

Max Lybbert
źródło
5
D & E to naprawdę dobra książka. Ale wygląda na to, że będę musiał go ponownie przeczytać - nie pamiętam żadnej historii.
Michael Burr,
2
Pamiętam trzy funkcje, które nie zostały zaakceptowane, omówione w D&E. Jest to pierwsza (wyszukaj „Michael Tiemann” w indeksie, aby znaleźć historię), reguła dwóch tygodni jest druga (wyszukaj „reguła dwóch tygodni” w indeksie), a trzecia została nazwana parametrami (wyszukaj „ nazwane argumenty ”w indeksie).
Max Lybbert,
12
typedefTechnika 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 potrzebujesz usingtego przypadku.
v.oddou
105

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ć.

class MyClass : public MyBase
{
private:  // Prevents erroneous use by other classes.
  typedef MyBase inherited;
...

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.

Roddy
źródło
2
Jeśli chodzi o tworzenie łańcuchów super :: super, jak wspomniałem w pytaniu, wciąż muszę znaleźć interesujące zastosowanie. Na razie widzę to tylko jako hack, ale warto o tym wspomnieć, choćby ze względu na różnice w Javie (gdzie nie można łączyć „super”).
paercebal,
4
Po kilku miesiącach zostałem przekonany do twojego punktu widzenia (i Miałem błąd z powodu zapomnianego „super”, jak wspomniałeś ...). Masz całkowitą rację w swojej odpowiedzi, w tym, jak sądzę, łańcuchów. ^ _ ^ ...
paercebal,
jak mam teraz tego używać do wywoływania metod klas nadrzędnych?
mLstudent33
oznacza to, że muszę zadeklarować wszystkie metody klasy Base, virtualjak pokazano tutaj: martinbroadhurst.com/typedef-super.html
mLstudent33
36

Jednym 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:

class Base
{
public:  virtual void foo() { ... }
};

class Derived: public Base
{
public:
    typedef Base super;
    virtual void foo()
    {
        super::foo();   // call superclass implementation

        // do other stuff
        ...
    }
};

class DerivedAgain: public Derived
{
public:
    virtual void foo()
    {
        // Call superclass function
        super::foo();    // oops, calls Base::foo() rather than Derived::foo()

        ...
    }
};

(Jak zauważył Martin York w komentarzach do tej odpowiedzi, problem ten można wyeliminować, czyniąc typedef prywatnym, a nie publicznym lub chronionym.)

Kristopher Johnson
źródło
Dziękuję za uwagę. Ten efekt uboczny umknął mojej uwadze. Chociaż prawdopodobnie nie skompilowałby się do użytku konstruktora, chyba gdzie indziej, błąd byłby obecny.
paercebal,
5
Jednak prywatny typedef zapobiegnie łączeniu w łańcuch, o którym mowa w oryginalnym poście.
AnT
1
Napotkanie tego właśnie błędu doprowadziło mnie do tego pytania :(
Steve Vermeulen,
więc użyj super1dla Base i super2Derived? DerivedAgain może korzystać z obu?
mLstudent33
20

FWIW Microsoft dodał rozszerzenie dla __super w swoim kompilatorze.

krujos
źródło
Kilku programistów zaczęło naciskać na użycie __super. Na początku odepchnąłem się, ponieważ czułem, że to „źle” i „niestandardowo”. JEDNAK uwielbiam to.
Aardvark,
8
Pracuję nad aplikacją dla systemu Windows i uwielbiam rozszerzenie __super. Zasmuca mnie to, że komitet normalizacyjny odrzucił go na rzecz sztuczki typedef wspomnianej tutaj, ponieważ chociaż ta sztuczka typedef jest dobra, wymaga większej konserwacji niż słowo kluczowe kompilatora podczas zmiany hierarchii dziedziczenia i poprawnie obsługuje wielokrotne dziedziczenie (nie wymagając dwóch typedefs jak super1 i super2). Krótko mówiąc, zgadzam się z innym komentatorem, że rozszerzenie MS jest BARDZO przydatne i każdy, kto korzysta wyłącznie z Visual Studio, powinien zdecydowanie rozważyć jego użycie.
Brian
15

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.

Colin Jensen
źródło
13

Właśnie znalazłem alternatywne obejście. Mam duży problem z typedef, który mnie dziś ugryzł:

  • Typedef wymaga dokładnej kopii nazwy klasy. Jeśli ktoś zmieni nazwę klasy, ale nie zmieni typedef, wtedy napotkasz problemy.

Więc wymyśliłem lepsze rozwiązanie przy użyciu bardzo prostego szablonu.

template <class C>
struct MakeAlias : C
{ 
    typedef C BaseAlias;
};

Więc teraz zamiast

class Derived : public Base
{
private:
    typedef Base Super;
};

ty masz

class Derived : public MakeAlias<Base>
{
    // Can refer to Base as BaseAlias here
};

W tym przypadku BaseAliasnie jest prywatny i starałem się uchronić przed nieostrożnym użyciem, wybierając nazwę typu, która powinna ostrzegać innych programistów.

zakleszczenie papieru
źródło
4
publicalias jest wadą, ponieważ jesteś otwarty na błąd wymieniony w odpowiedziach Roddy'ego i Kristophera (np. możesz (przez pomyłkę) wyprowadzić Derivedzamiast MakeAlias<Derived>)
Alexander Malakhov
3
Nie masz również dostępu do konstruktorów klas bazowych na listach inicjalizujących. (Można to zrekompensować w C ++ 11 za pomocą dziedziczenia konstruktorów MakeAliaslub idealnego przekazywania, ale wymaga to odniesienia się do MakeAlias<BaseAlias>konstruktorów, a nie bezpośrednio do Ckonstruktorów.)
Adam H. Peterson,
12

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.

Michael Burr
źródło
6
Głosuj za frazą: „nic nie mówi, że coś musi być przydatne wszędzie, aby było przydatne”.
Tanktalus,
1
Zgoda. To jest użyteczne. Właśnie patrzyłem na bibliotekę cech typu boost, aby zobaczyć, czy istnieje sposób na wygenerowanie typedef za pomocą szablonu. Niestety nie wydaje się, że możesz.
Ferruccio,
9

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(lub Base) zamiast super.

Ten idiom jest szczególnie użyteczny przy pracy z klasami szablonów. Jako przykład rozważ następującą klasę (z prawdziwego projektu ):

template <typename TText, typename TSpec>
class Finder<Index<TText, PizzaChili<TSpec> >, PizzaChiliFinder>
    : public Finder<Index<TText, PizzaChili<TSpec> >, Default>
{
    typedef Finder<Index<TText, PizzaChili<TSpec> >, Default> TBase;
    // …
}

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.

Konrad Rudolph
źródło
Ostatnio użyłem „super” z tego samego powodu. Dziedziczenie publiczne było zbyt bolesne, aby sobie z tym poradzić ... :-) ...
paercebal
4

Dość często widziałem, że jest używany, czasami jako super_t, gdy baza jest złożonym typem szablonu ( boost::iterator_adaptorrobi to na przykład)

James Hopkin
źródło
1
Masz rację. Tutaj znalazłem odniesienie. boost.org/doc/libs/1_36_0/libs/iterator/doc/…
paercebal,
4

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.

Toji
źródło
4
Jako kontrapunkt Perl ma zarówno wielokrotne dziedzictwo, jak i SUPER. Istnieje pewne zamieszanie, ale algorytm, przez który przechodzi maszyna wirtualna w celu znalezienia czegoś, jest wyraźnie udokumentowany. To powiedziawszy, rzadko widuję MI, gdy wiele klas bazowych oferuje te same metody, w których i tak może powstać zamieszanie.
Tanktalus,
1
Microsoft Visual Studio implementuje słowo kluczowe __super dla C ++, niezależnie od tego, czy jest to CLI. Jeśli tylko jedna z odziedziczonych klas oferuje metodę poprawnego podpisu (najczęstszy przypadek, jak wspomniano w Tanktalusie), to po prostu wybiera jedyny prawidłowy wybór. Jeśli dwie lub więcej odziedziczonych klas zapewnia dopasowanie funkcji, to nie działa i wymaga jawności.
Brian
3

Jednym z dodatkowych powodów użycia typedef dla nadklasy jest użycie złożonych szablonów w dziedziczeniu obiektu.

Na przykład:

template <typename T, size_t C, typename U>
class A
{ ... };

template <typename T>
class B : public A<T,99,T>
{ ... };

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 ++

jdkoftinoff
źródło
2

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.

Greg Hewgill
źródło
1

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.

Matt Dillard
źródło
1

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ą.

Scott Langham
źródło
1

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ą PrimaryParenti jest realizowane w następujący sposób:

template<typename BaseClass>
class PrimaryParent : virtual public BaseClass
{
protected:
    using super = BaseClass;
public:
    template<typename ...ArgTypes>
    PrimaryParent<BaseClass>(ArgTypes... args) : BaseClass(args...){}
}

Wtedy dowolna klasa, której chcesz użyć, zostanie zadeklarowana jako taka:

class MyObject : public PrimaryParent<SomeBaseClass>
{
public:
    MyObject() : PrimaryParent<SomeBaseClass>(SomeParams) {}
}

Aby uniknąć konieczności korzystania z wirtualnego dziedziczenia w PrimaryParentdniu BaseClass, konstruktor biorąc zmienną liczbę argumentów jest używany, aby umożliwić budowę BaseClass.

Powodem publicdziedziczenia BaseClassw PrimaryParentjest umożliwienie MyObjectpełnej kontroli nad spadkiem, BaseClasspomimo posiadania klasy pomocniczej między nimi.

Oznacza to, że każda klasa, którą chcesz mieć, supermusi korzystać z PrimaryParentklasy pomocniczej, a każde dziecko może dziedziczyć tylko z jednej klasy PrimaryParent(stąd nazwa).

Kolejnym ograniczeniem dla tej metody jest MyObjectdziedziczenie tylko jednej klasy, która dziedziczy po niej PrimaryParent, i którą należy odziedziczyć za pomocą PrimaryParent. Oto co mam na myśli:

class SomeOtherBase : public PrimaryParent<Ancestor>{}

class MixinClass {}

//Good
class BaseClass : public PrimaryParent<SomeOtherBase>, public MixinClass
{}


//Not Good (now 'super' is ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public SomeOtherBase{}

//Also Not Good ('super' is again ambiguous)
class MyObject : public PrimaryParent<BaseClass>, public PrimaryParent<SomeOtherBase>{}

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 zdefiniuj superw tej klasie i nie dziedzicz PrimaryParent.

Pomysł zdefiniowania superw każdej klasie nie jest dla mnie zbyt atrakcyjny, PrimaryParentponieważ pozwala super, 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ć.

Kevin
źródło
1

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”?

  1. super oznacza wywołanie ostatniego implementatora metody (nie metody bazowej)
  2. podstawa oznacza wybór domyślnej klasy w oparciu o wielokrotne dziedziczenie.

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:

#include <iostream>

// Library defiens 4 classes in typical library class hierarchy
class Abstract
{
public:
    virtual void f() = 0;
};

class LibraryBase1 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base1" << std::endl;
    }
};

class LibraryBase2 :
    virtual public Abstract
{
public:
    void f() override
    {
        std::cout << "Base2" << std::endl;
    }
};

class LibraryDerivate :
    public LibraryBase1,
    public LibraryBase2
{
    // base is meaningfull only for this class,
    // this class decides who is my base in multiple inheritance
private:
    using base = LibraryBase1;

protected:
    // this is super! base is not super but base!
    using super = LibraryDerivate;

public:
    void f() override
    {
        std::cout << "I'm super not my Base" << std::endl;
        std::cout << "Calling my *default* base: " << std::endl;
        base::f();
    }
};

// Library user
struct UserBase :
    public LibraryDerivate
{
protected:
    // NOTE: If user overrides f() he must update who is super, in one class before base!
    using super = UserBase; // this typedef is needed only so that most derived version
    // is called, which calls next super in hierarchy.
    // it's not needed here, just saying how to chain "super" calls if needed

    // NOTE: User can't call base, base is a concept private to each class, super is not.
private:
    using base = LibraryDerivate; // example of typedefing base.

};

struct UserDerived :
    public UserBase
{
    // NOTE: to typedef who is super here we would need to specify full name
    // when calling super method, but in this sample is it's not needed.

    // Good super is called, example of good super is last implementor of f()
    // example of bad super is calling base (but which base??)
    void f() override
    {
        super::f();
    }
};

int main()
{
    UserDerived derived;
    // derived calls super implementation because that's what
    // "super" is supposed to mean! super != base
    derived.f();

    // Yes it work with polymorphism!
    Abstract* pUser = new LibraryDerivate;
    pUser->f();

    Abstract* pUserBase = new UserBase;
    pUserBase->f();
}

Kolejnym ważnym punktem jest tutaj:

  1. połączenie polimorficzne: połączenia w dół
  2. super połączenie: połączenia w górę

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ę.

metablaster
źródło
0

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:

// some header.h

#define CLASS some_iterator
#define SUPER_CLASS some_const_iterator
#define SUPER static_cast<SUPER_CLASS&>(*this)

template<typename T>
class CLASS : SUPER_CLASS {
   typedef CLASS<T> class_type;

   class_type& operator++();
};

template<typename T>
typename CLASS<T>::class_type CLASS<T>::operator++(
   int)
{
   class_type copy = *this;

   // Macro
   ++SUPER;

   // vs

   // Typedef
   // super::operator++();

   return copy;
}

#undef CLASS
#undef SUPER_CLASS
#undef SUPER

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, superaby odtworzyć zachowanie widoczne w Javie, ale moim stylem kodowania jest używanie wszystkich wielkich liter w makrach.

Zhro
źródło