Statische virtuelle C++-Mitglieder?

Lesezeit: 12 Minuten

Benutzer-Avatar
Lebenslauf

Ist es in C++ möglich, eine Member-Funktion zu haben, die beides ist? static und virtual? Anscheinend gibt es keinen einfachen Weg, dies zu tun (static virtual member(); ist ein Kompilierungsfehler), aber gibt es zumindest eine Möglichkeit, den gleichen Effekt zu erzielen?

IE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

Es ist sinnvoll zu verwenden GetTypeInformation() beide auf einer Instanz (object->GetTypeInformation()) und in einer Klasse (SomeObject::GetTypeInformation()), was für Vergleiche nützlich und für Vorlagen von entscheidender Bedeutung sein kann.

Die einzige Möglichkeit, die mir einfällt, besteht darin, zwei Funktionen / eine Funktion und eine Konstante pro Klasse zu schreiben oder Makros zu verwenden.

Irgendwelche anderen Lösungen?

  • Nur eine Nebenbemerkung: Statische Methoden werden auf keiner Instanz ausgeführt, was bedeutet, dass sie nicht den impliziten this-Zeiger haben. Davon abgesehen, die const in einer Methodensignatur kennzeichnet das Implizite this Zeiger als Konstante und kann nicht auf statische Methoden angewendet werden, da ihnen der implizite Parameter fehlt.

    – David Rodríguez – Dribeas

    1. Dezember 2009 um 8:48 Uhr

  • @cvb: Ich würde ernsthaft darüber nachdenken, Ihr Beispiel durch Code zu ersetzen, der keine Reflexion beinhaltet. So wie es jetzt ist, verschmelzen Sie zwei getrennte (wenn auch verwandte) Probleme. Ja, und ich weiß, es ist fünfeinhalb Jahre her, seit du danach gefragt hast.

    – einpoklum

    24. April 2015 um 21:22 Uhr


  • Eines der hier implizit erforderlichen Features besteht darin, dass der Compiler überprüft, ob jedes Objekt in einer Hierarchie eine bestimmte Schnittstelle implementiert (wobei eine oder mehrere Methoden statisch sind). Grundsätzlich ist eine rein virtuelle Prüfung auf statische Methode sehr sinnvoll, denn wenn man vergisst die statische Methode hinzuzufügen, dann der Compiler sollen Fehler aus. virtuell ist hier nicht das Stichwort, sondern mehr abstrakt was zufällig eine Art Synonym in C++ ist, außer in diesem speziellen Fall. Leider ist dies derzeit mit C++ nicht möglich.

    – xryl669

    2. Juni 2017 um 13:23 Uhr


Nein, es gibt keine Möglichkeit, denn was würde passieren, wenn du anrufst Object::GetTypeInformation()? Sie kann nicht wissen, welche Version der abgeleiteten Klasse aufgerufen werden soll, da ihr kein Objekt zugeordnet ist.

Sie müssen es zu einer nicht statischen virtuellen Funktion machen, damit es richtig funktioniert; Wenn Sie auch die Version einer bestimmten abgeleiteten Klasse nicht virtuell ohne eine Objektinstanz aufrufen können möchten, müssen Sie auch eine zweite redundante statische nicht virtuelle Version bereitstellen.

  • Wenn Sie sich statische Klassen (oder statische Mitglieder von Klassen) als Singleton vorstellen, wird alles offensichtlich – in Ihrem Fall sollte einfach Object::GetTypeInformation aufgerufen werden – genauso wie beim Aufrufen einer regulären virtuellen Methode für die Basisklasse Beispiel. (Natürlich, Wenn Von C++ unterstützte virtuelle statische Methoden)

    – Spuk

    18. Dezember 2013 um 7:59 Uhr

  • Das ist ein völlig fadenscheiniges Argument. Wenn Sie die Klasse anstelle eines Objekts verwenden, würde es natürlich die Version dieser Klasse verwenden, anstatt den virtuellen Versand durchzuführen. Da ist nichts Neues.

    – Deduplizierer

    2. Februar 2015 um 22:17 Uhr

Viele sagen es geht nicht, ich würde noch einen Schritt weiter gehen und sagen es ist nicht sinnvoll.

Ein statisches Mitglied ist etwas, das sich nicht auf eine Instanz bezieht, sondern nur auf die Klasse.

Ein virtuelles Element bezieht sich nicht direkt auf eine Klasse, sondern nur auf eine Instanz.

Ein statisches virtuelles Mitglied wäre also etwas, das sich nicht auf eine Instanz oder Klasse bezieht.

  • Es ist vollkommen sinnvoll in Sprachen, in denen Klassen erstklassige Werte sind – zB Delphi hat das und hat auch “statische virtuelle” Methoden.

    – Pavel Minaev

    30. November 2009 um 16:04 Uhr

  • Ich denke auch, dass statische Virtuals sinnvoll sind. Es wäre möglich, Schnittstellenklassen zu definieren und statische Methoden einzuschließen, die in abgeleiteten Klassen implementiert werden müssen.

    – bkausbk

    24. Januar 2013 um 7:43 Uhr

  • Es ist nicht so aussagekräftig für a static virtual Methode, aber a static rein virtual Methode ist in einer Schnittstelle sehr sinnvoll.

    – Bret Kuhns

    11. Februar 2013 um 3:29 Uhr

  • Es ist durchaus sinnvoll, eine zu haben static const string MyClassSillyAdditionalName.

    – einpoklum

    20. August 2014 um 7:54 Uhr

  • Es ist völlig sinnvoll, wenn Sie den Compiler verwenden möchten, um sicherzustellen, dass Sie eine statische Methode in allen untergeordneten Klassen implementiert haben. @BretKuhns hat recht.

    – xryl669

    10. Oktober 2014 um 9:34 Uhr

Benutzer-Avatar
Nate CK

Ich bin neulich auf dieses Problem gestoßen: Ich hatte einige Klassen voller statischer Methoden, aber ich wollte Vererbung und virtuelle Methoden verwenden und die Codewiederholung reduzieren. Meine Lösung war:

Verwenden Sie statt statischer Methoden einen Singleton mit virtuellen Methoden.

Mit anderen Worten, jede Klasse sollte eine statische Methode enthalten, die Sie aufrufen, um einen Zeiger auf eine einzelne, gemeinsam genutzte Instanz der Klasse zu erhalten. Sie können die wahren Konstruktoren privat oder geschützt machen, damit externer Code sie nicht missbrauchen kann, indem Sie zusätzliche Instanzen erstellen.

In der Praxis ist die Verwendung eines Singletons der Verwendung statischer Methoden sehr ähnlich, außer dass Sie Vererbung und virtuelle Methoden nutzen können.

  • Das wird mich Leistung kosten – es sei denn, der Compiler kann sicher sein, dass: 1. es tatsächlich ein Singleton ist und 2. nichts davon erbt, ich glaube nicht, dass es den gesamten Overhead optimieren kann.

    – einpoklum

    24. April 2015 um 21:26 Uhr

  • Wenn Sie die Leistung solcher Dinge beunruhigt, dann ist C# wahrscheinlich die falsche Sprache für Sie.

    – Nate CK

    10. November 2015 um 20:59 Uhr

  • Ah, guter Punkt. Offensichtlich ist es eine Weile her, dass ich darüber nachgedacht habe, seit ich es 2009 geschrieben habe. Lassen Sie es mich anders ausdrücken: Wenn Sie diese Art von Performance-Sache beunruhigt, sollten Sie die Verwendung von Vererbung vielleicht vollständig vermeiden. Auf dem Poster wurde ausdrücklich nach virtuellen Methoden gefragt, daher ist es seltsam, dass Sie hierher kommen, um sich über den Overhead virtueller Methoden zu beschweren.

    – Nate CK

    11. November 2015 um 23:32 Uhr


Benutzer-Avatar
Timo

Während Alsk bereits eine ziemlich detaillierte Antwort gegeben hat, möchte ich eine Alternative hinzufügen, da ich denke, dass seine erweiterte Implementierung zu kompliziert ist.

Wir beginnen mit einer abstrakten Basisklasse, die die Schnittstelle für alle Objekttypen bereitstellt:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

Jetzt brauchen wir eine tatsächliche Implementierung. Aber um zu vermeiden, dass wir sowohl die statischen als auch die virtuellen Methoden schreiben müssen, lassen wir unsere eigentlichen Objektklassen die virtuellen Methoden erben. Dies funktioniert natürlich nur, wenn die Basisklasse weiß, wie sie auf die statische Elementfunktion zugreifen kann. Wir müssen also eine Vorlage verwenden und ihr den tatsächlichen Klassennamen des Objekts übergeben:

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

Schließlich müssen wir unsere realen Objekte implementieren. Hier müssen wir nur die statische Elementfunktion implementieren, die virtuellen Elementfunktionen werden von der ObjectImpl-Vorlagenklasse geerbt, die mit dem Namen der abgeleiteten Klasse instanziiert wird, sodass sie auf ihre statischen Elemente zugreift.

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

Fügen wir zum Testen Code hinzu:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

Nachtrag (12. Januar 2019):

Anstatt die Funktion GetClassNameStatic() zu verwenden, können Sie den Klassennamen auch als statisches Element definieren, sogar “inline”, was IIRC seit C++11 funktioniert (keine Angst vor all den Modifikatoren :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

Benutzer-Avatar
Alsk

Es ist möglich!

Aber was genau möglich ist, grenzen wir ein. Die Leute wollen oft eine Art “statische virtuelle Funktion”, weil der Code dupliziert wird, um dieselbe Funktion über den statischen Aufruf “SomeDerivedClass::myfunction()” und den polymorphen Aufruf “base_class_pointer->myfunction()” aufrufen zu können. Eine “legale” Methode, um eine solche Funktionalität zuzulassen, ist die Vervielfältigung von Funktionsdefinitionen:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

Was ist, wenn die Basisklasse eine große Anzahl statischer Funktionen hat und die abgeleitete Klasse jede von ihnen überschreiben muss und man vergessen hat, eine duplizierende Definition für die virtuelle Funktion bereitzustellen? Richtig, wir werden währenddessen einen seltsamen Fehler bekommen Laufzeit was schwer nachzuvollziehen ist. Ursache Duplizierung von Code ist eine schlechte Sache. Das Folgende versucht, dieses Problem zu lösen (und ich möchte vorher sagen, dass es vollständig typsicher ist und keine schwarze Magie wie die von typeid oder dynamic_cast enthält 🙂

Wir wollen also nur eine Definition von getTypeInformation() pro abgeleiteter Klasse bereitstellen, und es ist offensichtlich, dass es eine Definition von sein muss statisch Funktion, da es nicht möglich ist, “SomeDerivedClass::getTypeInformation()” aufzurufen, wenn getTypeInformation() virtuell ist. Wie können wir die statische Funktion einer abgeleiteten Klasse über einen Zeiger auf die Basisklasse aufrufen? Mit vtable ist dies nicht möglich, da vtable nur Zeiger auf virtuelle Funktionen speichert und wir uns entschieden haben, keine virtuellen Funktionen zu verwenden, können wir vtable nicht zu unserem Vorteil ändern. Um dann über einen Zeiger auf die Basisklasse auf die statische Funktion für die abgeleitete Klasse zugreifen zu können, müssen wir irgendwie den Typ eines Objekts in seiner Basisklasse speichern. Ein Ansatz besteht darin, die Basisklasse mithilfe von “seltsam wiederkehrenden Vorlagenmustern” zu erstellen, aber dies ist hier nicht angemessen, und wir verwenden eine Technik namens “Typlöschung”:

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

Jetzt können wir den Typ eines Objekts in der Basisklasse “Object” mit einer Variablen “Keeper” speichern:

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

In einer abgeleiteten Klasse muss Keeper während der Konstruktion initialisiert werden:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

Lassen Sie uns syntaktischen Zucker hinzufügen:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

Jetzt sehen die Erklärungen der Nachkommen so aus:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

Verwendungszweck:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

Vorteile:

  1. weniger Duplizierung von Code (aber wir müssen OVERRIDE_STATIC_FUNCTIONS in jedem Konstruktor aufrufen)

Nachteile:

  1. OVERRIDE_STATIC_FUNCTIONS in jedem Konstruktor
  2. Speicher- und Performance-Overhead
  3. erhöhte Komplexität

Offene Punkte:

1) Es gibt unterschiedliche Namen für statische und virtuelle Funktionen, wie kann man hier Mehrdeutigkeiten lösen?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) Wie kann man OVERRIDE_STATIC_FUNCTIONS implizit in jedem Konstruktor aufrufen?

  • +1 für den Aufwand, obwohl ich mir nicht sicher bin, ob dies eleganter ist, als die Funktionalität nur mit virtuellen Methoden an ein Singleton zu delegieren.

    – einpoklum

    20. August 2014 um 8:00 Uhr

  • @einpoklum, ich kann mir eine Situation vorstellen, in der dies vorzuziehen ist. Angenommen, wir haben eine Menge Client-Code, der bereits statische Methoden aufruft. Der Wechsel von statischen Methoden zu einem Singleton mit virtuellen Methoden würde Änderungen im Clientcode erfordern, während die oben vorgestellte Lösung nicht invasiv ist.

    –Alsk

    22. August 2014 um 12:56 Uhr

  • Das Schlüsselwort “virtual” ist für “Foo::getTypeInformation” und “TypeKeeperImpl::getTypeInformation” nicht erforderlich.

    – bartolo-otrit

    11. November 2014 um 11:28 Uhr

Es ist möglich. Machen Sie zwei Funktionen: statisch und virtuell

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

  • +1 für den Aufwand, obwohl ich mir nicht sicher bin, ob dies eleganter ist, als die Funktionalität nur mit virtuellen Methoden an ein Singleton zu delegieren.

    – einpoklum

    20. August 2014 um 8:00 Uhr

  • @einpoklum, ich kann mir eine Situation vorstellen, in der dies vorzuziehen ist. Angenommen, wir haben eine Menge Client-Code, der bereits statische Methoden aufruft. Der Wechsel von statischen Methoden zu einem Singleton mit virtuellen Methoden würde Änderungen im Clientcode erfordern, während die oben vorgestellte Lösung nicht invasiv ist.

    –Alsk

    22. August 2014 um 12:56 Uhr

  • Das Schlüsselwort “virtual” ist für “Foo::getTypeInformation” und “TypeKeeperImpl::getTypeInformation” nicht erforderlich.

    – bartolo-otrit

    11. November 2014 um 11:28 Uhr

Nein, das ist nicht möglich, da statischen Elementfunktionen a fehlt this Zeiger. Und statische Mitglieder (sowohl Funktionen als auch Variablen) sind per se nicht wirklich Klassenmitglieder. Sie werden einfach von aufgerufen ClassName::member, und halten Sie sich an die Klassenzugriffsbezeichner. Ihre Speicherung wird irgendwo außerhalb der Klasse definiert; Der Speicher wird nicht jedes Mal erstellt, wenn Sie ein Objekt der Klasse instanziieren. Zeiger auf Klassenmitglieder sind speziell in Semantik und Syntax. Ein Zeiger auf ein statisches Element ist in jeder Hinsicht ein normaler Zeiger.

virtuelle Funktionen in einer Klasse benötigt die this Zeiger und ist stark an die Klasse gekoppelt, daher können sie nicht statisch sein.

  • Nur nicht statische Funktionen benötigen a this Zeiger. Statische Funktionen sind nicht spezifisch für eine Instanz und würden sie nicht benötigen. Das ist also kein Grund, warum virtuelle statische Mitglieder unmöglich sind.

    – einpoklum

    3. Februar 2016 um 19:49 Uhr

1013400cookie-checkStatische virtuelle C++-Mitglieder?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy