met behulp van “super” in C++

Mijn stijl van codering omvat het volgende idioom:

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

Hiermee kunt u “Super” gebruiken als een alias om bijvoorbeeld in Constructors:

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

Of zelfs bij het bellen van de methode van de basisklasse in zijn overgedragen versie:

void Derived::foo()
{
   super::foo() ;
   // ... And then, do something else
}

Het kan zelfs geketend zijn (ik heb echter nog steeds het gebruik voor dat):

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
}

Hoe dan ook, ik vind het gebruik van “typedef super” erg handig, bijvoorbeeld, wanneer base eenvoudig en / of gedemoneerd is.

Het feit is dat super wordt geïmplementeerd in Java, evenals in C # (waar het “basis” wordt genoemd, tenzij ik het mis heb). Maar C++ mist dit zoekwoord.

Dus mijn vragen:

  • is dit gebruik van typedef super gemeenschappelijke / zeldzame / nooit gezien in de code waarmee je werkt?
  • is dit gebruik van typedef super ok (d.w.z. zie je sterke of niet zo sterke redenen om het niet te gebruiken)?
  • zou “super” een goede zaak moeten zijn, moet het enigszins gestandaardiseerd zijn in C++, of is dit gebruik via een typedef al voldoende?

Bewerken:Roddy noemde het feit dat de typedef privé zou moeten zijn. Dit zou betekenen dat een afgeleide klasse deze niet zou kunnen gebruiken zonder deze opnieuw te declareren. Maar ik denk dat het ook de super::super chaining zou voorkomen (maar wie gaat daar nou om huilen?).

Bewerken 2:Nu, enkele maanden na het massaal gebruik van “super”, ben ik het volledig eens met Roddy’s standpunt: “super” zou privé moeten zijn. Ik zou zijn antwoord twee keer omhoog stemmen, maar ik denk dat ik dat niet kan.


Antwoord 1, autoriteit 100%

Bjarne Stroustrup vermeldt in Design and Evolution of C++dat superals trefwoord werd beschouwd door de ISO C++ Standards-commissie toen C++ voor het eerst werd gestandaardiseerd.

Dag Bruck stelde deze extensie voor en noemde de basisklasse ‘overgeërfd’. Het voorstel noemde het probleem van meervoudige overerving en zou dubbelzinnig gebruik hebben gemarkeerd. Zelfs Stroustrup was overtuigd.

Na discussie schreef Dag Bruck (ja, dezelfde persoon die het voorstel deed) dat het voorstel uitvoerbaar was, technisch goed, en vrij van grote gebreken, en meervoudige overerving afhandelde. Aan de andere kant was er niet genoeg waar voor het geld, en de commissie zou een neteliger probleem moeten aanpakken.

Michael Tiemann kwam laat aan en liet toen zien dat een getypte super prima zou werken, met dezelfde techniek waar in dit bericht naar werd gevraagd.

Dus nee, dit zal waarschijnlijk nooit gestandaardiseerd worden.

Als je geen exemplaar hebt, is Design and Evolutionde coverprijs zeker waard. Gebruikte exemplaren zijn verkrijgbaar voor ongeveer $ 10.


Antwoord 2, autoriteit 66%

Ik heb altijd “geërfd” gebruikt in plaats van super. (Waarschijnlijk vanwege een Delphi-achtergrond), en ik maak het altijd privé , om het probleem te voorkomen wanneer de ‘geërfd’ ten onrechte is weggelaten uit een klasse, maar een subklasse probeert het te gebruiken.

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

Mijn standaard ‘codesjabloon’ voor het maken van nieuwe klassen omvat de typedef, dus ik heb weinig gelegenheid om het per ongeluk weg te laten.

Ik denk niet dat het geketende “Super :: Super” -suggestie een goed idee is – als je dat doet, ben je waarschijnlijk heel moeilijk vast aan een bepaalde hiërarchie en verandert het waarschijnlijk slecht .


3, Autoriteit 22%

Een probleem hiermee is dat als u vergeet om super voor afgeleide klassen te (opnieuw te definiëren voor afgeleide klassen, dan elke oproep naar Super :: iets zal het boete compileren, maar zal waarschijnlijk de gewenste functie niet bellen.

Bijvoorbeeld:

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()
        ...
    }
};

(Zoals aangegeven door Martin York in de opmerkingen van dit antwoord, kan dit probleem worden geëlimineerd door het typedef privé te maken in plaats van openbaar of beschermd.)


4, Autoriteit 13%

FWIW Microsoft heeft een extensie toegevoegd voor __superin hun compiler.


Antwoord 5, autoriteit 9%

Super (of geërfd) is erg goed, want als je nog een overervingslaag tussen Base en Derived moet plakken, hoef je maar twee dingen te veranderen: 1. de “class Base: foo” en 2. de typedef

Als ik het me goed herinner, overwoog de C++ Standards-commissie om hier een trefwoord voor toe te voegen… totdat Michael Tiemann erop wees dat deze typedef-truc werkt.

Wat betreft meervoudige overerving, aangezien het onder de controle van de programmeur staat, kun je doen wat je wilt: misschien super1 en super2, of wat dan ook.


Antwoord 6, autoriteit 8%

Ik kan me niet herinneren dat ik dit eerder heb gezien, maar op het eerste gezicht vind ik het leuk. Zoals Ferruccioopmerkt, werkt het niet goed in het aangezicht van MI, maar MI is meer uitzondering dan regel en er is niets dat zegt dat iets overal bruikbaar moet zijn om bruikbaar te zijn.


Antwoord 7, autoriteit 8%

Ik heb zojuist een alternatieve oplossing gevonden. Ik heb een groot probleem met de typedef-aanpak die me vandaag beet:

  • De typedef vereist een exacte kopie van de klassenaam. Als iemand de klassenaam verandert, maar het typedef niet, dan krijg je problemen.

Dus ik bedacht een betere oplossing met een heel eenvoudig sjabloon.

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

Dus nu, in plaats van

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

je hebt

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

In dit geval is BaseAliasniet privé en ik heb geprobeerd me te beschermen tegen onzorgvuldig gebruik door een typenaam te selecteren die andere ontwikkelaars zou moeten waarschuwen.


Antwoord 8, autoriteit 5%

Ik heb dit idioom in veel codes gebruikt en ik ben er vrij zeker van dat ik het zelfs ergens in de bibliotheken van Boost heb gezien. Voor zover ik me herinner is de meest voorkomende naam echter Base(of Base) in plaats van super.

Dit idioom is vooral handig als u met sjabloonklassen werkt. Beschouw als voorbeeld de volgende klasse (van een echt project):

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

Let niet op de grappige namen. Het belangrijke punt hier is dat de overervingsketen typeargumenten gebruikt om compile-time polymorfisme te bereiken. Helaas wordt het nesting-niveau van deze sjablonen behoorlijk hoog. Daarom zijn afkortingen cruciaal voor de leesbaarheid en onderhoudbaarheid.


Antwoord 9, autoriteit 2%

Ik heb het vaak gebruikt, soms als super_t, wanneer de basis een complex sjabloontype is (boost::iterator_adaptordoet dit bijvoorbeeld)


Antwoord 10, autoriteit 2%

komt dit gebruik van typedef veel voor/zeldzaam/nooit voor in de code waarmee u werkt?

Ik heb dit specifieke patroon nog nooit gezien in de C++-code waarmee ik werk, maar dat betekent niet dat het er niet is.

is dit gebruik van typedef super ok (d.w.z. zie je sterke of minder sterke redenen om het niet te gebruiken)?

Het staat geen meervoudige overerving toe (in ieder geval netjes).

zou “super” een goede zaak moeten zijn, moet het enigszins gestandaardiseerd zijn in C++, of is dit gebruik via een typedef al voldoende?

Om de hierboven genoemde reden (meervoudige overerving), nee. De reden waarom u “super” ziet in de andere talen die u opsomt, is dat ze alleen enkele overerving ondersteunen, dus er is geen verwarring over waar “super” naar verwijst. Toegegeven, in die talen IS het nuttig, maar het heeft niet echt een plaats in het C++-gegevensmodel.

O, en ter info: C++/CLI ondersteunt dit concept in de vorm van het sleutelwoord “__super”. Houd er echter rekening mee dat C++/CLI ook geen meervoudige overerving ondersteunt.


Antwoord 11, autoriteit 2%

Een extra reden om een typedef voor de superklasse te gebruiken, is wanneer u complexe sjablonen gebruikt in de overerving van het object.

Bijvoorbeeld:

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

In klasse B zou het ideaal zijn om een typedef voor A te hebben, anders zou je het overal herhalen waar je naar de leden van A wilt verwijzen.

In deze gevallen kan het ook werken met meervoudige overerving, maar je zou geen typedef hebben met de naam ‘super’, het zou ‘base_A_t’ of iets dergelijks heten.

–jeffk++


Antwoord 12

Na de migratie van Turbo Pascal naar C++ vroeger, deed ik dit om een equivalent te hebben voor het Turbo Pascal “inherited” trefwoord, dat op dezelfde manier werkt. Maar na een paar jaar programmeren in C++ ben ik ermee gestopt. Ik merkte dat ik het gewoon niet zo erg nodig had.


Antwoord 13

Ik weet niet of het zeldzaam is of niet, maar ik heb zeker hetzelfde gedaan.

Zoals is opgemerkt, is de moeilijkheid om dit deel van de taal zelf te maken, wanneer een klasse gebruikmaakt van meervoudige overerving.


Antwoord 14

Ik gebruik dit af en toe. Net als ik merk dat ik het type basisklasse een paar keer typ, zal ik het vervangen door een typedef dat vergelijkbaar is met het jouwe.

Ik denk dat het een goed gebruik kan zijn. Zoals je zegt, als je basisklasse een sjabloon is, kan het typen besparen. Ook kunnen sjabloonklassen argumenten aannemen die fungeren als beleid voor hoe de sjabloon zou moeten werken. U bent vrij om het basistype te wijzigen zonder al uw verwijzingen ernaar te moeten corrigeren, zolang de interface van het basisstation compatibel blijft.

Ik denk dat het gebruik via de typedef al voldoende is. Ik zie niet in hoe het hoe dan ook in de taal zou worden ingebouwd, omdat meervoudige overerving betekent dat er veel basisklassen kunnen zijn, dus je kunt het naar eigen goeddunken typen voor de klasse die logischerwijs de belangrijkste basisklasse is.


15

Ik zal niet veel zeggen behalve de huidige code met opmerkingen die laat zien dat Super niet betekent dat het bellende basis is!

super != base.

Kortom, wat is “Super” toch verondersteld te betekenen? En dan zou wat ‘basis’ bedoeld zijn?

  1. SUPER betekent, de laatste implementatie van een methode (geen basismethode) roept
  2. basismiddelen, het kiezen van welke klasse standaard basis is in meerdere erfenis.

Deze 2 regels zijn van toepassing op in klasse typedefs.

Denk aan bibliotheek-implementator en bibliotheekgebruiker, wie is super en wie is basis?

voor meer informatie is hier werkcode voor kopiëren en plakken in uw 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();
}

Nog een belangrijk punt hier is dit:

  1. polymorfe oproep: oproepen naar beneden
  2. Super Call: roept u naar boven

Inside main()We gebruiken Polymorfe-oproepuitwisselingen die super belt naar boven, niet echt nuttig in het echte leven, maar het toont het verschil.


16

Ik gebruik het trefwoord __super. Maar het is Microsoft Specifiek:

http://msdn.microsoft.com/en-us/library /94dw1w7x.aspx


17

Dit is een methode die ik gebruik dat Macros gebruikt in plaats van een typedef. Ik weet dat dit niet de C++ manier is om dingen te doen, maar het kan handig zijn bij het samenvatten van iterators door erfenis wanneer alleen de basisklasse het verste omlaag de hiërarchie handelt.

Bijvoorbeeld:

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

De generieke setup die ik gebruik maakt het heel gemakkelijk om te lezen en te kopiëren/plakken tussen de overervingsboom die dubbele code heeft maar moet worden overschreven omdat het retourtype moet overeenkomen met de huidige klasse.

Je zou een kleine superkunnen gebruiken om het gedrag van Java te repliceren, maar mijn codeerstijl is om alle hoofdletters voor macro’s te gebruiken.

Other episodes