Statische virtuelle C ++ - Mitglieder?

140

Ist es in C ++ möglich, eine Mitgliedsfunktion zu haben, die sowohl staticals als auch ist virtual? Anscheinend gibt es keinen einfachen Weg, dies zu tun ( static virtual member();ist ein Kompilierungsfehler), aber gibt es zumindest einen Weg, um 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, GetTypeInformation()sowohl eine Instanz ( object->GetTypeInformation()) als auch eine Klasse ( SomeObject::GetTypeInformation()) zu verwenden, was für Vergleiche nützlich und für Vorlagen von entscheidender Bedeutung sein kann.

Ich kann mir nur vorstellen, zwei Funktionen / eine Funktion und eine Konstante pro Klasse zu schreiben oder Makros zu verwenden.

Irgendwelche anderen Lösungen?

cvb
quelle
12
Nur ein Nebenkommentar: Statische Methoden werden auf keiner Instanz ausgeführt, was bedeutet, dass sie nicht den impliziten Zeiger haben. Abgesehen davon kennzeichnet die constSignatur in einer Methode den impliziten thisZeiger als konstant und kann nicht auf statische Methoden angewendet werden, da ihnen der implizite Parameter fehlt.
David Rodríguez - Dribeas
2
@cvb: Ich würde ernsthaft überlegen, 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 Sie danach gefragt haben.
Einpoklum
Eine der hier implizit erforderlichen Funktionen besteht darin, dass der Compiler überprüft, ob jedes Objekt in einer Hierarchie eine bestimmte Schnittstelle implementiert (wobei eine oder mehrere der Methoden statisch sind). Grundsätzlich ist eine reine virtuelle Überprüfung auf statische Methoden sehr sinnvoll, da der Compiler einen Fehler verursachen sollte , wenn Sie vergessen, die statische Methode hinzuzufügen . virtuell ist hier nicht das Schlüsselwort, es ist abstrakter, was in C ++ eine Art Synonym ist, außer in diesem speziellen Fall. Leider können Sie dies derzeit nicht mit C ++ tun.
xryl669

Antworten:

75

Nein, es gibt keine Möglichkeit, denn was würde passieren, wenn Sie anrufen Object::GetTypeInformation()? Es kann nicht wissen, welche abgeleitete Klassenversion aufgerufen werden soll, da kein Objekt damit verknüpft ist.

Sie müssen es zu einer nicht statischen virtuellen Funktion machen, um ordnungsgemäß zu funktionieren. Wenn Sie die Version einer bestimmten abgeleiteten Klasse auch nicht virtuell ohne Objektinstanz aufrufen möchten, müssen Sie auch eine zweite redundante statische nicht virtuelle Version bereitstellen.

Adam Rosenfield
quelle
8
Wenn Sie statischer Klasse denken (oder Klassen statische Mitglieder) als Singleton, wird alles klar - in Ihrem Fall einfach Object :: GetTypeInformation aufgerufen werden soll - genauso wie regelmäßige virtuelle Methode auf Basisklasse aufrufen Instanz . (Natürlich, wenn C ++ virtuelle statische Methoden unterstützt)
Spook
13
Das ist ein völlig falsches Argument. Wenn Sie die Klasse anstelle eines Objekts verwenden, wird natürlich die Version dieser Klasse verwendet, anstatt den virtuellen Versand durchzuführen. Nichts Neues da.
Deduplikator
54

Viele sagen, es sei nicht möglich, ich würde noch einen Schritt weiter gehen und sagen, es sei nicht sinnvoll.

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

Ein virtuelles Mitglied 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 eine Klasse bezieht.

Rasmus Kaj
quelle
42
Dies ist in Sprachen, in denen Klassen erstklassige Werte sind, von großer Bedeutung - z. B. hat Delphi diese und auch "statische virtuelle" Methoden.
Pavel
4
Genau. Eine "virtuelle Funktion" ist (per Definition) eine Funktion, die dynamisch verknüpft ist , dh zur Laufzeit abhängig vom dynamischen Typ eines bestimmten Objekts ausgewählt wird. Daher kein Objekt = kein virtueller Aufruf.
Kos
7
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
34
Es ist nicht so aussagekräftig für eine static virtualMethode, aber eine static reine virtual Methode ist in einer Schnittstelle sehr aussagekräftig.
Bret Kuhns
4
Es ist absolut sinnvoll, eine zu haben static const string MyClassSillyAdditionalName.
Einpoklum
23

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 anstelle 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 abzurufen. Sie können die echten Konstruktoren privat oder geschützt machen, damit externer Code sie nicht missbrauchen kann, indem Sie zusätzliche Instanzen erstellen.

In der Praxis ähnelt die Verwendung eines Singletons der Verwendung statischer Methoden, mit der Ausnahme, dass Sie Vererbung und virtuelle Methoden nutzen können.

Nate CK
quelle
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 denke nicht, dass es den gesamten Overhead weg optimieren kann.
Einpoklum
Wenn Sie sich wegen der Leistung dieser Art Sorgen machen, ist C # wahrscheinlich die falsche Sprache für Sie.
Nate CK
3
Ah, guter Punkt. Offensichtlich ist es eine Weile her, dass ich darüber nachgedacht habe, seit ich es 2009 geschrieben habe. Lassen Sie mich das anders sagen: Wenn Sie sich über diese Art von Performance-Sache Sorgen machen, sollten Sie vielleicht die Verwendung von Vererbung ganz vermeiden. Das Poster hat speziell nach virtuellen Methoden gefragt, daher ist es seltsam, dass Sie hierher kommen, um sich über den Overhead virtueller Methoden zu beschweren.
Nate CK
15

Es ist möglich!

Aber was genau möglich ist, wollen wir eingrenzen. Menschen möchten häufig eine Art "statische virtuelle Funktion", da der Code doppelt vorhanden ist, um dieselbe Funktion durch den statischen Aufruf "SomeDerivedClass :: myfunction ()" und den polymorphen Aufruf "base_class_pointer-> myfunction ()" aufrufen zu können. Die "legale" Methode zum Zulassen einer solchen Funktionalität ist das Duplizieren 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 doppelte Definition für die virtuelle Funktion anzugeben? Richtig, wir werden zur Laufzeit einen seltsamen Fehler bekommen der schwer zu finden ist. Das Duplizieren 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 typeid's oder dynamic_cast's enthält :)

Wir möchten also nur eine Definition von getTypeInformation () pro abgeleiteter Klasse bereitstellen, und es ist offensichtlich, dass es sich um eine Definition von handeln muss statisch handeln mussFunktion, da "SomeDerivedClass :: getTypeInformation ()" nicht aufgerufen werden kann, wenn getTypeInformation () virtuell ist. Wie können wir die statische Funktion der abgeleiteten Klasse durch einen Zeiger auf die Basisklasse aufrufen? Mit vtable ist dies nicht möglich, da vtable nur Zeiger auf virtuelle Funktionen speichert. Da 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 der abgeleiteten Klasse zugreifen zu können, müssen wir den Typ eines Objekts in seiner Basisklasse speichern. Ein Ansatz besteht darin, die Basisklasse mithilfe eines "merkwürdig wiederkehrenden Vorlagenmusters" als Vorlage zu erstellen. Dies ist hier jedoch 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 der Keeper während der Erstellung 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"; }
};

Fügen wir syntaktischen Zucker hinzu:

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

Nun sehen Erklärungen von Nachkommen aus wie:

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"; }
};

Verwendung:

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 Leistungsaufwand
  3. erhöhte Komplexität

Offene Punkte:

1) Es gibt verschiedene 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 in jedem Konstruktor implizit aufrufen?

Alsk
quelle
+1 für Mühe, obwohl ich nicht sicher bin, ob dies eleganter ist, als nur die Funktionalität mit virtuellen Methoden an einen Singleton zu delegieren.
Einpoklum
1
@einpoklum, ich kann mir eine Situation vorstellen, in der dies vorzuziehen ist. Angenommen, wir haben viel 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
Das Schlüsselwort "virtual" ist für "Foo :: getTypeInformation" und "TypeKeeperImpl :: getTypeInformation" nicht erforderlich.
Bartolo-Otrit
12

Obwohl 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. Um jedoch zu vermeiden, dass sowohl die statische als auch die virtuelle Methode geschrieben werden müssen, erben unsere tatsächlichen Objektklassen die virtuellen Methoden. Dies funktioniert natürlich nur, wenn die Basisklasse weiß, wie sie auf die statische Elementfunktion zugreift. Wir müssen also eine Vorlage verwenden und den tatsächlichen Objektklassennamen an diese ü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, damit auf die statischen Elemente zugegriffen werden kann.

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 als "Inline", was IIRC seit C ++ 11 funktioniert (lassen Sie sich nicht von allen Modifikatoren erschrecken :)):

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

    // ...
};
Timo
quelle
11

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
};
Alexey Malistov
quelle
4
Statische Methoden können auch nicht const sein. Es macht einfach keinen Sinn, welche Instanz werden sie nicht mutieren?
David Rodríguez - Dribeas
1
Dies ist meist nur eine Codeduplizierung. Die Idee ist, dass die Unterklassen nur das statische const-Mitglied haben müssen und keinen Code, der darauf zugreift.
Einpoklum
8

Nein, dies ist nicht möglich, da statischen Elementfunktionen ein thisZeiger fehlt . Und statische Elemente (sowohl Funktionen als auch Variablen) sind an sich keine wirklichen Klassenmitglieder. Sie werden zufällig von aufgerufen ClassName::memberund halten sich an die Klassenzugriffsspezifizierer. Ihr Speicher wird irgendwo außerhalb der Klasse definiert; Der Speicher wird nicht jedes Mal erstellt, wenn Sie ein Objekt der Klasse instanziiert haben. 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ötigen den thisZeiger und sind sehr stark an die Klasse gekoppelt, daher können sie nicht statisch sein.


quelle
1
Nur nicht statische Funktionen benötigen einen this Zeiger. statische Funktionen sind nicht instanzspezifisch und würden sie nicht benötigen. Das ist also kein Grund, warum virtuelle statische Mitglieder unmöglich sind.
Einpoklum
7

Nun, eine ziemlich späte Antwort, aber es ist möglich, das merkwürdig wiederkehrende Vorlagenmuster zu verwenden. Dieser Wikipedia- Artikel enthält die Informationen, die Sie benötigen, und auch das Beispiel unter statischem Polymorphismus ist das, wonach Sie gefragt werden.

tropicana
quelle
3

Ich denke, was Sie versuchen zu tun, kann durch Vorlagen getan werden. Ich versuche hier zwischen den Zeilen zu lesen. Sie versuchen, eine Methode aus einem Code aufzurufen, in dem eine abgeleitete Version aufgerufen wird, der Aufrufer jedoch nicht angibt, welche Klasse. Beispiel:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

Sie möchten, dass Try () die Bar-Version von M aufruft, ohne Bar anzugeben. Für die Statik verwenden Sie eine Vorlage. Also ändere es so:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}
zumalifeguard
quelle
1
Wenn Sie Ihren Code 4 Leerzeichen einrücken, können Sie ihn automatisch formatieren lassen. Alternativ glaube ich, dass Sie das Zurück-Häkchen verwenden können, um den gleichen Zweck inline zu erreichen.
Chollida
1
Das ist das Offensichtliche, was ich vermisst habe. Danke dir. Dennoch Scham sind Mitglieder seltsam.
allesblinkt
M () ist keine statische Funktion. wie heißt es T :: M ()?
DDukDDak99
3

Nein, die statische Elementfunktion kann nicht virtuell sein, da das virtuelle Konzept zur Laufzeit mit Hilfe von vptr aufgelöst wird und vptr kein statisches Mitglied einer Klasse ist. Aufgrund dieser statischen Elementfunktion kann vptr nicht auf statisches Element zugreifen, so dass statisches Element dies kann Sei nicht virtuell.

Prabhat Kumar
quelle
2
Nur instanzspezifische virtuelle Methoden erfordern die vtable der Instanzen. Sie könnten eine statische vtable (eine pro Klasse) haben. Und wenn Sie möchten, dass Instanzen davon erfahren, zeigen Sie einfach von der vtable der Instanz auch auf die vtable der Klassenstatik.
Einpoklum
2

Es ist nicht möglich, aber das liegt nur an einer Auslassung. Es ist nicht etwas, das "keinen Sinn ergibt", wie viele Leute zu behaupten scheinen. Um es klar zu sagen, ich spreche von so etwas:

struct Base {
  static virtual void sayMyName() {
    cout << "Base\n";
  }
};

struct Derived : public Base {
  static void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
  Derived::sayMyName(); // Also would work.
}

Das ist 100% etwas , das könnte umgesetzt werden (es hat einfach nicht), und ich würde etwas argumentieren , das nützlich ist.

Überlegen Sie, wie normale virtuelle Funktionen funktionieren. Entfernen Sie das statics und fügen Sie einige andere Sachen hinzu und wir haben:

struct Base {
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  void sayMyName() override {
    cout << "Derived\n";
  }
};

void foo(Base *b) {
  b->sayMyName();
}

Dies funktioniert einwandfrei. Im Grunde genommen erstellt der Compiler zwei Tabellen, die als VTables bezeichnet werden, und weist den virtuellen Funktionen wie diesen Indizes zu

enum Base_Virtual_Functions {
  sayMyName = 0;
  foo = 1;
};

using VTable = void*[];

const VTable Base_VTable = {
  &Base::sayMyName,
  &Base::foo
};

const VTable Derived_VTable = {
  &Derived::sayMyName,
  &Base::foo
};

Als nächstes wird jede Klasse mit virtuellen Funktionen um ein anderes Feld erweitert, das auf ihre VTable verweist, sodass der Compiler sie grundsätzlich so ändert:

struct Base {
  VTable* vtable;
  virtual void sayMyName() {
    cout << "Base\n";
  }
  virtual void foo() {
  }
  int somedata;
};

struct Derived : public Base {
  VTable* vtable;
  void sayMyName() override {
    cout << "Derived\n";
  }
};

Was passiert dann eigentlich, wenn Sie anrufen b->sayMyName()? Grundsätzlich ist dies:

b->vtable[Base_Virtual_Functions::sayMyName](b);

(Der erste Parameter wird this.)

Ok, gut, wie würde es mit statischen virtuellen Funktionen funktionieren? Was ist der Unterschied zwischen statischen und nicht statischen Elementfunktionen? Der einzige Unterschied besteht darin, dass letztere einen thisZeiger erhalten.

Mit statischen virtuellen Funktionen können wir genau das Gleiche tun - entfernen Sie einfach den thisZeiger.

b->vtable[Base_Virtual_Functions::sayMyName]();

Dies könnte dann beide Syntaxen unterstützen:

b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".

Also ignoriere alle Neinsager. Es macht Sinn. Warum wird es dann nicht unterstützt? Ich denke, das liegt daran, dass es nur sehr wenig Nutzen hat und sogar etwas verwirrend sein kann.

Der einzige technische Vorteil gegenüber einer normalen virtuellen Funktion besteht darin, dass Sie nicht thisan die Funktion übergeben müssen, aber ich denke nicht, dass dies einen messbaren Unterschied für die Leistung bedeuten würde.

Es bedeutet, dass Sie keine separate statische und nicht statische Funktion für Fälle haben, in denen Sie eine Instanz haben und wenn Sie keine Instanz haben, aber es kann auch verwirrend sein, dass es nur wirklich "virtuell" ist, wenn Sie es verwenden der Instanzaufruf.

Timmmm
quelle
0

Nein, dies ist nicht möglich, da statische Elemente zur Kompilierungszeit gebunden werden, während virtuelle Mitglieder zur Laufzeit gebunden werden.

PaulJWilliams
quelle
0

Erstens sind die Antworten richtig, dass das, was das OP anfordert, ein Widerspruch ist: Virtuelle Methoden hängen vom Laufzeittyp einer Instanz ab; Insbesondere hängen statische Funktionen nicht von einer Instanz ab, sondern nur von einem Typ. Es ist jedoch sinnvoll, dass statische Funktionen etwas Spezifisches für einen Typ zurückgeben. Zum Beispiel hatte ich eine Familie von MouseTool-Klassen für das Statusmuster und ich fing an, dass jede eine statische Funktion hat, die den dazugehörigen Tastaturmodifikator zurückgibt. Ich habe diese statischen Funktionen in der Factory-Funktion verwendet, die die richtige MouseTool-Instanz erstellt hat. Diese Funktion überprüfte den Mausstatus anhand von MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier () usw. und instanziierte dann den entsprechenden. Natürlich wollte ich später überprüfen, ob der Zustand stimmt, also wollte ich etwas schreiben wie "

Wenn Sie dies wünschen, möchten Sie vielleicht Ihre Lösung überarbeiten. Trotzdem verstehe ich den Wunsch, statische Methoden zu haben und sie dann basierend auf dem dynamischen Typ einer Instanz dynamisch aufzurufen. Ich denke, das Besuchermuster kann Ihnen geben, was Sie wollen. Es gibt dir was du willst. Es ist ein bisschen zusätzlicher Code, aber er könnte für andere Besucher nützlich sein.

Hintergrundinformationen finden Sie unter: http://en.wikipedia.org/wiki/Visitor_pattern .

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

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

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

Dann für jedes konkrete Objekt:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

und definieren Sie dann den Basisbesucher:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

Dann der konkrete Besucher, der die entsprechende statische Funktion auswählt:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

Verwenden Sie es schließlich:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

Anmerkungen:

  • Konstanz als Übung verlassen.
  • Sie haben eine Referenz von einer statischen zurückgegeben. Wenn Sie keinen Singleton haben, ist das fraglich.

Wenn Sie Kopier- und Einfügefehler vermeiden möchten, bei denen eine Ihrer Besuchsmethoden die falsche statische Funktion aufruft, können Sie eine Vorlagenhilfefunktion (die selbst nicht virtuell sein kann) für Ihren Besucher mit einer Vorlage wie der folgenden verwenden:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};
Ben
quelle
Wenn virtuelle statische Methoden vorhanden wären, würden sie von nichts in einer Instanz abhängen - aber die Instanz müsste ihren Typ kennen, um sie aufzurufen. Dies kann von einem Compiler ausgearbeitet werden (z. B. durch Verwendung einer einzelnen Datenstruktur pro Klasse mit Zeigern auf virtuelle statische Methoden und Elemente). Dies ist sicherlich kein Widerspruch.
Einpoklum
Ob es sich um einen Widerspruch handelt oder nicht, ist eine Frage der Semantik. Man könnte sich vorstellen, dass C ++ das Aufrufen von Statik von einer Instanz aus zulässt (z. B. Foo foo; ... foo::bar();anstelle von Foo::bar();). Das ist nicht anders, decltype(foo)::bar();aber das wäre wieder statisch gebunden. Der Besucheransatz scheint ein vernünftiger Weg zu sein, um dieses Verhalten zu erreichen, ohne nur die statische Methode zu einer virtuellen const-Methode zu machen.
Ben
0

Mit c ++ können Sie die statische Vererbung mit der crt-Methode verwenden. In diesem Beispiel wird es häufig für Fenstervorlagen atl & wtl verwendet.

Siehe https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Um einfach zu sein, haben Sie eine Klasse, die wie die Klasse myclass von sich selbst erstellt wurde: public myancestor. Ab diesem Punkt kann die myancestor-Klasse jetzt Ihre statische T :: YourImpl-Funktion aufrufen.


quelle
-1

Vielleicht können Sie meine Lösung unten ausprobieren:

class Base {
public:
    Base(void);
    virtual ~Base(void);

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}
Jiez
quelle
Ja, ich weiß, 4 Jahre. Erklären Sie den -score für diejenigen, die den Code nicht in so vielen Details lesen möchten. Base::mSelfbezieht sich auf die aktuellste Instanz einer abgeleiteten Klasse, selbst wenn diese Instanz zerstört wurde . so class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */das ist nicht das, was gewünscht wird.
Jesse Chisholm
-3

Wie andere gesagt haben, gibt es zwei wichtige Informationen:

  1. thisBeim statischen Funktionsaufruf und gibt es keinen Zeiger
  2. Der thisZeiger zeigt auf die Struktur, in der die virtuelle Tabelle oder der Thunk verwendet wird, um nach der aufzurufenden Laufzeitmethode zu suchen.

Eine statische Funktion wird zur Kompilierungszeit bestimmt.

Ich habe dieses Codebeispiel in statischen C ++ - Elementen in der Klasse gezeigt . Es zeigt, dass Sie eine statische Methode mit einem Nullzeiger aufrufen können:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}
Chollida
quelle
6
Technisch gesehen ist dies undefiniertes Verhalten. Sie können einen Nullzeiger aus keinem Grund zurückstellen. Das einzige, was Sie mit einem Nullzeiger tun können, ist a) ihm einen anderen Zeiger zuzuweisen und b) ihn mit einem anderen Zeiger zu vergleichen.
KeithB
1
Darüber hinaus können Sie es nur vergleichen , für die Gleichstellung . (Oder inequality_ mit einem anderen Zeiger, nicht Ordnung Dh p < null, p >= nulletc sind alle auch nicht definiert.
Pavel Minaev
1
@KeithB - ​​Der Vollständigkeit halber können Sie delete auch sicher für einen Nullzeiger aufrufen.
Steve Rowe