Ist es in C ++ möglich, eine Mitgliedsfunktion zu haben, die sowohl static
als 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?
const
Signatur in einer Methode den implizitenthis
Zeiger als konstant und kann nicht auf statische Methoden angewendet werden, da ihnen der implizite Parameter fehlt.Antworten:
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.
quelle
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.
quelle
static virtual
Methode, aber einestatic
reinevirtual
Methode ist in einer Schnittstelle sehr aussagekräftig.static const string MyClassSillyAdditionalName
.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.
quelle
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:
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":
Jetzt können wir den Typ eines Objekts in der Basisklasse "Object" mit einer Variablen "keeper" speichern:
In einer abgeleiteten Klasse muss der Keeper während der Erstellung initialisiert werden:
Fügen wir syntaktischen Zucker hinzu:
Nun sehen Erklärungen von Nachkommen aus wie:
Verwendung:
Vorteile:
Nachteile:
Offene Punkte:
1) Es gibt verschiedene Namen für statische und virtuelle Funktionen. Wie kann man hier Mehrdeutigkeiten lösen?
2) Wie kann man OVERRIDE_STATIC_FUNCTIONS in jedem Konstruktor implizit aufrufen?
quelle
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:
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:
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.
Fügen wir zum Testen Code hinzu:
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 :)):
quelle
Es ist möglich. Machen Sie zwei Funktionen: statisch und virtuell
quelle
Nein, dies ist nicht möglich, da statischen Elementfunktionen ein
this
Zeiger fehlt . Und statische Elemente (sowohl Funktionen als auch Variablen) sind an sich keine wirklichen Klassenmitglieder. Sie werden zufällig von aufgerufenClassName::member
und 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
this
Zeiger und sind sehr stark an die Klasse gekoppelt, daher können sie nicht statisch sein.quelle
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.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.
quelle
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:
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:
quelle
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.
quelle
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:
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
static
s und fügen Sie einige andere Sachen hinzu und wir haben: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
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:
Was passiert dann eigentlich, wenn Sie anrufen
b->sayMyName()
? Grundsätzlich ist dies:(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
this
Zeiger erhalten.Mit statischen virtuellen Funktionen können wir genau das Gleiche tun - entfernen Sie einfach den
this
Zeiger.Dies könnte dann beide Syntaxen unterstützen:
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
this
an 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.
quelle
Nein, dies ist nicht möglich, da statische Elemente zur Kompilierungszeit gebunden werden, während virtuelle Mitglieder zur Laufzeit gebunden werden.
quelle
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 .
Dann für jedes konkrete Objekt:
und definieren Sie dann den Basisbesucher:
Dann der konkrete Besucher, der die entsprechende statische Funktion auswählt:
Verwenden Sie es schließlich:
Anmerkungen:
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:
quelle
Foo foo; ... foo::bar();
anstelle vonFoo::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.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
Vielleicht können Sie meine Lösung unten ausprobieren:
quelle
Base::mSelf
bezieht sich auf die aktuellste Instanz einer abgeleiteten Klasse, selbst wenn diese Instanz zerstört wurde . soclass 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.Wie andere gesagt haben, gibt es zwei wichtige Informationen:
this
Beim statischen Funktionsaufruf und gibt es keinen Zeigerthis
Zeiger 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:
quelle
p < null
,p >= null
etc sind alle auch nicht definiert.