Verwenden von "super" in C ++

203

Mein Codierungsstil umfasst die folgende Redewendung:

class Derived : public Base
{
   public :
      typedef Base super; // note that it could be hidden in
                          // protected/private section, instead

      // Etc.
} ;

Dies ermöglicht es mir, "super" als Alias ​​für Base zu verwenden, beispielsweise in Konstruktoren:

Derived(int i, int j)
   : super(i), J(j)
{
}

Oder sogar beim Aufrufen der Methode aus der Basisklasse in ihrer überschriebenen Version:

void Derived::foo()
{
   super::foo() ;

   // ... And then, do something else
}

Es kann sogar verkettet werden (ich muss jedoch noch die Verwendung dafür finden):

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
}

Wie auch immer, ich finde die Verwendung von "typedef super" sehr nützlich, zum Beispiel wenn Base entweder ausführlich und / oder mit Vorlagen versehen ist.

Tatsache ist, dass Super sowohl in Java als auch in C # implementiert ist (wo es "base" heißt, sofern ich mich nicht irre). In C ++ fehlt dieses Schlüsselwort.

Also meine Fragen:

  • Ist diese Verwendung von typedef in dem Code, mit dem Sie arbeiten, sehr häufig / selten / nie zu sehen?
  • ist diese Verwendung von typedef super Ok (dh sehen Sie starke oder nicht so starke Gründe, es nicht zu verwenden)?
  • sollte "super" eine gute Sache sein, sollte es in C ++ etwas standardisiert sein, oder reicht diese Verwendung durch ein typedef bereits aus?

Edit: Roddy erwähnte die Tatsache, dass das typedef privat sein sollte. Dies würde bedeuten, dass eine abgeleitete Klasse sie nicht verwenden kann, ohne sie erneut zu deklarieren. Aber ich denke, es würde auch die Super :: Super-Verkettung verhindern (aber wer wird dafür weinen?).

Edit 2: Nun, einige Monate nachdem ich "super" massiv benutzt habe, stimme ich Roddys Standpunkt voll und ganz zu: "super" sollte privat sein. Ich würde seine Antwort zweimal positiv bewerten, aber ich denke, ich kann nicht.

paercebal
quelle
Genial! Genau das, wonach ich gesucht habe. Ich glaube nicht, dass ich diese Technik bisher jemals anwenden musste. Hervorragende Lösung für meinen plattformübergreifenden Code.
AlanKley
6
Für mich supersieht es so aus Javaund es ist nichts Schlimmes, aber ... C++unterstützt aber Mehrfachvererbung.
ST3
2
@ user2623967: Richtig. Bei einfacher Vererbung reicht ein "Super" aus. Wenn Sie mehrere Vererbungen haben, ist "superA", "superB" usw. eine gute Lösung: Sie möchten die Methode von der einen oder anderen Implementierung aus aufrufen, also müssen Sie TELLEN, welche Implementierung Sie möchten. Wenn Sie "super" -ähnliches typedef verwenden, können Sie einen leicht zugänglichen / beschreibbaren Namen angeben (anstatt beispielsweise MyFirstBase<MyString, MyStruct<MyData, MyValue>>überall zu schreiben )
paercebal
Beachten Sie, dass Sie beim Erben von einer Vorlage die Vorlagenargumente nicht angeben müssen, wenn Sie darauf verweisen. Zum Beispiel: template <class baz> struct Foo {...void bar() {...} ...}; struct Foo2: Foo<AnnoyinglyLongListOfArguments> { void bar2() { ... Foo::bar(); ...} };Das hat bei mir mit gcc 9.1 funktioniert --std = c ++ 1y (c ++ 14).
Thanasis Papoutsidakis
1
... Ähm, Korrektur. Es scheint in jedem c ++ Standard zu funktionieren, nicht nur in 14.
Thanasis Papoutsidakis

Antworten:

151

Bjarne Stroustrup erwähnt in Design und Evolution von C ++, dass superdas ISO C ++ - Normungskomitee das erste Mal, als C ++ standardisiert wurde , als Schlüsselwort in Betracht gezogen wurde.

Dag Bruck schlug diese Erweiterung vor und nannte die Basisklasse "geerbt". Der Vorschlag erwähnte das Problem der Mehrfachvererbung und hätte mehrdeutige Verwendungen gekennzeichnet. Auch Stroustrup war überzeugt.

Nach der Diskussion schrieb Dag Bruck (ja, dieselbe Person, die den Vorschlag gemacht hat), dass der Vorschlag umsetzbar, technisch einwandfrei und frei von größeren Mängeln sei und mehrere Vererbungen handhabte. Auf der anderen Seite gab es nicht genug Geld, und das Komitee sollte sich mit einem heiklen Problem befassen.

Michael Tiemann kam spät an und zeigte dann, dass ein typisierter Super mit der gleichen Technik, nach der in diesem Beitrag gefragt wurde, gut funktionieren würde.

Also, nein, das wird wahrscheinlich nie standardisiert.

Wenn Sie keine Kopie haben, ist Design and Evolution den Deckungspreis wert. Gebrauchte Exemplare sind für etwa 10 US-Dollar erhältlich.

Max Lybbert
quelle
5
D & E ist in der Tat ein gutes Buch. Aber es scheint, als müsste ich es noch einmal lesen - ich erinnere mich an keine der beiden Geschichten.
Michael Burr
2
Ich erinnere mich an drei Funktionen, die in D & E nicht akzeptiert wurden. Dies ist die erste (siehe "Michael Tiemann" im Index, um die Geschichte zu finden), die Zwei-Wochen-Regel ist die zweite (siehe "Zwei-Wochen-Regel" im Index) und die dritte wurde als Parameter bezeichnet (nachschlagen " benannte Argumente "im Index).
Max Lybbert
12
Es gibt einen großen Fehler in der typedefTechnik: Sie respektiert DRY nicht. Der einzige Weg wäre, hässliche Makros zu verwenden, um Klassen zu deklarieren. Wenn Sie erben, kann die Basis eine lange Vorlagenklasse mit mehreren Parametern oder noch schlimmer sein. (zB mehrere Klassen) Sie müssten das alles ein zweites Mal umschreiben. Schließlich sehe ich ein großes Problem mit Vorlagenbasen, die Argumente für Vorlagenklassen enthalten. In diesem Fall ist der Super eine Vorlage (und keine Instanziierung einer Vorlage). Welches kann nicht typisiert werden. Auch in C ++ 11 benötigen Sie usingfür diesen Fall.
v.oddou
105

Ich habe immer "geerbt" anstatt super verwendet. (Wahrscheinlich aufgrund eines Delphi-Hintergrunds), und ich mache es immer privat , um das Problem zu vermeiden, wenn das 'geerbte' fälschlicherweise in einer Klasse weggelassen wird, aber eine Unterklasse versucht, es zu verwenden.

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

Meine Standard-Codevorlage zum Erstellen neuer Klassen enthält das typedef, sodass ich kaum Gelegenheit habe, es versehentlich wegzulassen.

Ich denke nicht, dass der verkettete "super :: super" -Vorschlag eine gute Idee ist. Wenn Sie das tun, sind Sie wahrscheinlich sehr stark an eine bestimmte Hierarchie gebunden, und wenn Sie sie ändern, wird das wahrscheinlich schlecht.

Roddy
quelle
2
Wie ich in der Frage erwähnt habe, muss ich für die Verkettung von super :: super noch eine interessante Verwendung finden. Im Moment sehe ich es nur als Hack, aber es war erwähnenswert, schon allein wegen der Unterschiede zu Java (wo man "super" nicht verketten kann).
Paercebal
4
Nach ein paar Monaten wurde ich zu Ihrem Standpunkt konvertiert (und ich hatte einen Fehler wegen eines vergessenen "Super", wie Sie erwähnt haben ...). Sie haben völlig Recht mit Ihrer Antwort, einschließlich der Verkettung, denke ich. ^ _ ^ ...
paercebal
Wie verwende ich dies jetzt, um übergeordnete Klassenmethoden aufzurufen?
mLstudent33
Bedeutet das, dass ich alle Methoden der Basisklassevirtual wie hier gezeigt deklarieren
muss
36

Ein Problem dabei ist, dass, wenn Sie vergessen, Super für abgeleitete Klassen (neu) zu definieren, jeder Aufruf von Super :: Something gut kompiliert wird, aber wahrscheinlich nicht die gewünschte Funktion aufruft.

Beispielsweise:

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()

        ...
    }
};

(Wie Martin York in den Kommentaren zu dieser Antwort hervorhob, kann dieses Problem behoben werden, indem das typedef eher privat als öffentlich oder geschützt gemacht wird.)

Kristopher Johnson
quelle
Danke für die Bemerkung. Dieser Nebeneffekt war mir entgangen. Während es wahrscheinlich nicht für die Verwendung durch den Konstruktor kompiliert werden würde, würde der Fehler wohl überall dort sein.
Paercebal
5
Private typedef verhindert jedoch die verkettete Verwendung, wie im ursprünglichen Beitrag erwähnt.
AnT
1
Genau auf diesen Fehler zu stoßen, hat mich zu dieser Frage geführt :(
Steve Vermeulen
also super1für Base und super2in Derived verwenden? Der DerivedAgain kann beides verwenden?
mLstudent33
20

FWIW Microsoft hat in seinem Compiler eine Erweiterung für __super hinzugefügt .

krujos
quelle
Einige Entwickler hier haben versucht, __super zu verwenden. Zuerst schob ich mich zurück, da ich das Gefühl hatte, es sei "falsch" und "nicht standardisiert". Ich habe es jedoch immer mehr geliebt.
Aardvark
8
Ich arbeite an einer Windows-App und liebe die Erweiterung __super. Es macht mich traurig, dass das Standardkomitee es zugunsten des hier erwähnten typedef-Tricks abgelehnt hat, denn obwohl dieser typedef-Trick gut ist, erfordert er mehr Wartung als ein Compiler-Schlüsselwort, wenn Sie die Vererbungshierarchie ändern, und behandelt die Mehrfachvererbung korrekt (ohne zwei zu benötigen) typedefs wie super1 und super2). Kurz gesagt, ich stimme dem anderen Kommentator zu, dass die MS-Erweiterung SEHR nützlich ist, und jeder, der ausschließlich Visual Studio verwendet, sollte nachdrücklich in Betracht ziehen, sie zu verwenden.
Brian
15

Super (oder geerbt) ist eine sehr gute Sache, denn wenn Sie eine weitere Vererbungsebene zwischen Base und Derived einfügen müssen, müssen Sie nur zwei Dinge ändern: 1. die "Klasse Base: foo" und 2. die typedef

Wenn ich mich richtig erinnere, erwog das C ++ - Standardkomitee, ein Schlüsselwort dafür hinzuzufügen ... bis Michael Tiemann darauf hinwies, dass dieser typedef-Trick funktioniert.

Was die Mehrfachvererbung betrifft, können Sie, da sie vom Programmierer gesteuert wird, tun, was Sie wollen: vielleicht super1 und super2 oder was auch immer.

Colin Jensen
quelle
13

Ich habe gerade eine alternative Problemumgehung gefunden. Ich habe ein großes Problem mit dem typedef-Ansatz, das mich heute gebissen hat:

  • Das typedef erfordert eine genaue Kopie des Klassennamens. Wenn jemand den Klassennamen ändert, aber das typedef nicht ändert, treten Probleme auf.

Also habe ich eine bessere Lösung mit einer sehr einfachen Vorlage gefunden.

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

Also jetzt statt

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

du hast

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

In diesem Fall BaseAliasist es nicht privat und ich habe versucht, mich vor unachtsamer Verwendung zu schützen, indem ich einen Typnamen ausgewählt habe, der andere Entwickler alarmieren soll.

Papierstau
quelle
4
publicAlias ​​ist ein Nachteil, da Sie offen für den Fehler sind, der in Roddys und Kristopher's Antworten erwähnt wird (z. B. können Sie (aus Versehen) von Derivedstatt ableiten MakeAlias<Derived>)
Alexander Malakhov
3
Sie haben auch keinen Zugriff auf Basisklassenkonstruktoren in Ihren Initialisierungslisten. (Dies kann in C ++ 11 durch Erben von Konstruktoren in MakeAliasoder perfekte Weiterleitung kompensiert werden. Sie müssen jedoch auf die MakeAlias<BaseAlias>Konstruktoren verweisen, anstatt nur Cdirekt auf die Konstruktoren zu verweisen .)
Adam H. Peterson,
12

Ich erinnere mich nicht, dass ich das vorher gesehen habe, aber auf den ersten Blick gefällt es mir. Wie Ferruccio bemerkt, funktioniert es angesichts von MI nicht gut, aber MI ist eher die Ausnahme als die Regel und es gibt nichts, was besagt, dass etwas überall verwendbar sein muss, um nützlich zu sein.

Michael Burr
quelle
6
Stimmen Sie nur für den Satz ab: "Es gibt nichts, was besagt, dass etwas überall verwendbar sein muss, um nützlich zu sein."
Tanktalus
1
Einverstanden. Es ist nützlich. Ich habe mir gerade die Boost-Typ-Merkmalsbibliothek angesehen, um zu sehen, ob es eine Möglichkeit gibt, das Typedef über eine Vorlage zu generieren. Leider scheint es nicht so, als ob du es kannst.
Ferruccio
9

Ich habe diese Redewendung in vielen Codes gesehen und bin mir ziemlich sicher, dass ich sie sogar irgendwo in Boosts Bibliotheken gesehen habe. Soweit ich mich erinnere, ist der gebräuchlichste Name jedoch base(oder Base) anstelle von super.

Diese Redewendung ist besonders nützlich, wenn Sie mit Vorlagenklassen arbeiten. Betrachten Sie als Beispiel die folgende Klasse (aus einem realen Projekt ):

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;
    // …
}

Kümmere dich nicht um die lustigen Namen. Der wichtige Punkt hierbei ist, dass die Vererbungskette Typargumente verwendet, um einen Polymorphismus zur Kompilierungszeit zu erzielen. Leider wird die Verschachtelungsebene dieser Vorlagen ziemlich hoch. Abkürzungen sind daher entscheidend für die Lesbarkeit und Wartbarkeit.

Konrad Rudolph
quelle
Ich hatte das "Super" in letzter Zeit aus dem gleichen Grund benutzt. Das öffentliche Erbe war zu schmerzhaft, um es anders zu handhaben ... :-) ...
paercebal
4

Ich habe es ziemlich oft gesehen, manchmal als super_t, wenn die Basis ein komplexer Vorlagentyp ist ( boost::iterator_adaptortut dies zum Beispiel)

James Hopkin
quelle
1
Du hast recht. Ich habe die Referenz hier gefunden. boost.org/doc/libs/1_36_0/libs/iterator/doc/…
paercebal
4

Ist diese Verwendung von typedef in dem Code, mit dem Sie arbeiten, sehr häufig / selten / nie zu sehen?

Ich habe dieses spezielle Muster in dem C ++ - Code, mit dem ich arbeite, noch nie gesehen, aber das bedeutet nicht, dass es nicht da draußen ist.

ist diese Verwendung von typedef super Ok (dh sehen Sie starke oder nicht so starke Gründe, es nicht zu verwenden)?

Mehrfachvererbung ist nicht möglich (jedenfalls sauber).

sollte "super" eine gute Sache sein, sollte es in C ++ etwas standardisiert sein, oder reicht diese Verwendung durch ein typedef bereits aus?

Aus dem oben genannten Grund (Mehrfachvererbung), Nr. Der Grund, warum Sie "super" in den anderen von Ihnen aufgelisteten Sprachen sehen, ist, dass sie nur die Einzelvererbung unterstützen, sodass keine Verwirrung darüber besteht, worauf sich "super" bezieht. Zugegeben, in diesen Sprachen ist es nützlich, aber es hat keinen Platz im C ++ - Datenmodell.

Oh, und zu Ihrer Information: C ++ / CLI unterstützt dieses Konzept in Form des Schlüsselworts "__super". Beachten Sie jedoch, dass C ++ / CLI auch keine Mehrfachvererbung unterstützt.

Toji
quelle
4
Als Kontrapunkt hat Perl sowohl Mehrfachvererbung als auch SUPER. Es gibt einige Verwirrung, aber der Algorithmus, den die VM durchläuft, um etwas zu finden, ist klar dokumentiert. Trotzdem sehe ich MI selten dort, wo mehrere Basisklassen die gleichen Methoden anbieten, bei denen ohnehin Verwirrung entstehen könnte.
Tanktalus
1
Microsoft Visual Studio implementiert das Schlüsselwort __super für C ++, unabhängig davon, ob es sich um eine CLI handelt. Wenn nur eine der geerbten Klassen eine Methode für die korrekte Signatur bietet (der häufigste Fall, wie von Tanktalus erwähnt), wird nur die einzig gültige Auswahl getroffen. Wenn zwei oder mehr geerbte Klassen eine Funktionsübereinstimmung liefern, funktioniert dies nicht und erfordert, dass Sie explizit sind.
Brian
3

Ein weiterer Grund für die Verwendung eines typedef für die Oberklasse besteht darin, dass Sie komplexe Vorlagen für die Vererbung des Objekts verwenden.

Zum Beispiel:

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

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

In Klasse B wäre es ideal, ein typedef für A zu haben, sonst würden Sie es nicht wiederholen können, wo immer Sie auf die Mitglieder von A verweisen möchten.

In diesen Fällen kann es auch mit Mehrfachvererbung funktionieren, aber Sie hätten kein typedef mit dem Namen 'super', es würde 'base_A_t' oder so ähnlich heißen.

--jeffk ++

jdkoftinoff
quelle
2

Nachdem ich früher von Turbo Pascal nach C ++ migriert war, habe ich dies getan, um ein Äquivalent für das "geerbte" Schlüsselwort von Turbo Pascal zu erhalten, das auf die gleiche Weise funktioniert. Nachdem ich einige Jahre in C ++ programmiert hatte, hörte ich damit auf. Ich fand, ich brauchte es einfach nicht sehr.

Greg Hewgill
quelle
1

Ich weiß nicht, ob es selten ist oder nicht, aber ich habe auf jeden Fall das Gleiche getan.

Wie bereits erwähnt, besteht die Schwierigkeit, diesen Teil der Sprache selbst zu erstellen, darin, dass eine Klasse die Mehrfachvererbung verwendet.

Matt Dillard
quelle
1

Ich benutze dies von Zeit zu Zeit. Wenn ich den Basisklassentyp ein paar Mal abtippe, werde ich ihn durch einen Typedef ersetzen, der Ihrem ähnlich ist.

Ich denke, es kann eine gute Verwendung sein. Wie Sie sagen, wenn Ihre Basisklasse eine Vorlage ist, kann sie die Eingabe sparen. Vorlagenklassen können auch Argumente annehmen, die als Richtlinien für die Funktionsweise der Vorlage dienen. Sie können den Basistyp ändern, ohne alle Ihre Verweise darauf korrigieren zu müssen, solange die Schnittstelle der Basis kompatibel bleibt.

Ich denke die Verwendung durch das typedef ist schon genug. Ich kann sowieso nicht sehen, wie es in die Sprache eingebaut werden würde, da Mehrfachvererbung bedeutet, dass es viele Basisklassen geben kann. Sie können es also nach Belieben für die Klasse eingeben, die Ihrer Meinung nach die wichtigste Basisklasse ist.

Scott Langham
quelle
1

Ich habe versucht, genau das gleiche Problem zu lösen. Ich habe ein paar Ideen herumgeschmissen, wie die Verwendung variabler Vorlagen und die Packerweiterung, um eine beliebige Anzahl von Eltern zu berücksichtigen, aber mir wurde klar, dass dies zu einer Implementierung wie 'super0' und 'super1' führen würde. Ich habe es weggeworfen, weil das kaum nützlicher wäre, als es von Anfang an nicht zu haben.

Meine Lösung umfasst eine Hilfsklasse PrimaryParentund wird folgendermaßen implementiert:

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

Dann wird jede Klasse, die Sie verwenden möchten, als solche deklariert:

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

Um zu vermeiden, dass die virtuelle Vererbung in PrimaryParenton verwendet werden muss BaseClass, wird ein Konstruktor verwendet, der eine variable Anzahl von Argumenten verwendet, um die Konstruktion von zu ermöglichen BaseClass.

Der Grund für die publicVererbung von BaseClassinto PrimaryParentbesteht darin, MyObjectdie volle Kontrolle über die Vererbung von in zu haben, BaseClassobwohl eine Helferklasse zwischen ihnen besteht.

Dies bedeutet, dass jede Klasse, die Sie haben möchten, die superHilfsklasse verwenden muss PrimaryParentund jedes Kind nur von einer Klasse mit erben darf PrimaryParent(daher der Name).

Eine weitere Einschränkung für diese Methode besteht darin, dass MyObjectnur eine Klasse PrimaryParentgeerbt werden kann, von der geerbt wird, und dass eine Klasse mit geerbt werden muss PrimaryParent. Folgendes meine ich:

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>{}

Bevor Sie dies aufgrund der scheinbaren Anzahl von Einschränkungen und der Tatsache, dass es zwischen jeder Erbschaft eine Mittelklasse gibt, als Option verwerfen, sind diese Dinge nicht schlecht.

Mehrfachvererbung ist ein starkes Werkzeug, aber in den meisten Fällen gibt es nur einen Hauptelternteil, und wenn es andere Elternteile gibt, handelt es sich wahrscheinlich um Mixin-Klassen oder Klassen, die PrimaryParentsowieso nicht erben . Wenn immer noch eine Mehrfachvererbung erforderlich ist (obwohl in vielen Situationen die Verwendung der Komposition zum Definieren eines Objekts anstelle der Vererbung von Vorteil wäre), definieren Sie superdiese Klasse nur explizit und erben Sie nicht von PrimaryParent.

Die Idee, zu definieren , superin jeder Klasse für mich nicht sehr ansprechend, mit PrimaryParentermöglicht super, eindeutig ein inheritence basierte alias, in der Klassendefinition Linie zu bleiben , anstatt die Klasse Körper , wo die Daten gehen.

Das könnte aber nur ich sein.

Natürlich ist jede Situation anders, aber berücksichtigen Sie diese Dinge, die ich gesagt habe, wenn Sie sich für eine Option entscheiden.

Kevin
quelle
1

Ich werde nicht viel sagen, außer den vorliegenden Code mit Kommentaren, die zeigen, dass Super nicht bedeutet, Base anzurufen!

super != base.

Kurz gesagt, was soll "super" überhaupt bedeuten? und was soll dann "base" bedeuten?

  1. super bedeutet, den letzten Implementierer einer Methode aufzurufen (keine Basismethode)
  2. Basis bedeutet, auszuwählen, welche Klasse die Standardbasis bei Mehrfachvererbung ist.

Diese beiden Regeln gelten für Typedefs in Klassen.

Betrachten Sie den Bibliotheksimplementierer und den Bibliotheksbenutzer, wer ist super und wer ist die Basis?

Für weitere Informationen finden Sie hier den Arbeitscode zum Kopieren und Einfügen in Ihre 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();
}

Ein weiterer wichtiger Punkt hier ist:

  1. polymorpher Aufruf: Aufruf nach unten
  2. Superanruf: Anrufe nach oben

Im Inneren verwenden main()wir polymorphe Call Downards, die nach oben rufen, im wirklichen Leben nicht wirklich nützlich, aber den Unterschied demonstrieren.

Metablaster
quelle
0

Dies ist eine Methode, die ich verwende und die Makros anstelle eines typedef verwendet. Ich weiß, dass dies nicht die C ++ - Methode ist, aber es kann praktisch sein, wenn Iteratoren durch Vererbung miteinander verkettet werden, wenn nur die Basisklasse, die am weitesten unten in der Hierarchie liegt, auf einen geerbten Offset einwirkt.

Beispielsweise:

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

Das generische Setup, das ich verwende, erleichtert das Lesen und Kopieren / Einfügen zwischen dem Vererbungsbaum, der doppelten Code enthält, aber überschrieben werden muss, da der Rückgabetyp mit der aktuellen Klasse übereinstimmen muss.

Man könnte Kleinbuchstaben verwenden, superum das in Java beobachtete Verhalten zu replizieren, aber mein Codierungsstil besteht darin, alle Großbuchstaben für Makros zu verwenden.

Zhro
quelle