Virtuelle Funktion
Im Objekt orientierte Programmierung, in Sprachen wie z. C ++, und Objekt Pascal, a Virtuelle Funktion oder Virtuelle Methode ist eine vererbbare und überschrieben Funktion oder Methode für welche Dynamischer Versand ist erleichtert. Dieses Konzept ist ein wichtiger Teil der (Laufzeit) Polymorphismus Teil von Objekt orientierte Programmierung (OOP). Kurz gesagt, eine virtuelle Funktion definiert eine Zielfunktion, die ausgeführt werden soll, das Ziel ist jedoch möglicherweise nicht zur Kompilierungszeit bekannt.
Die meisten Programmiersprachen wie z. JavaScript, Php und PythonBehandeln Sie alle Methoden standardmäßig als virtuell[1][2] und liefern keinen Modifikator, um dieses Verhalten zu ändern. Einige Sprachen bieten jedoch Modifikatoren, um zu verhindern, dass Methoden durch abgeleitete Klassen überschrieben werden (wie die Finale Schlüsselwort in Java[3] und Php[4]).
Zweck
Das Konzept der virtuellen Funktion löst das folgende Problem:
Im Objekt orientierte Programmierung, Wenn eine abgeleitete Klasse von einer Basisklasse erbt, kann ein Objekt der abgeleiteten Klasse über a erwähnt werden Zeiger oder Hinweis des Basisklassentyps anstelle des abgeleiteten Klassentyps. Wenn die von der abgeleiteten Klasse überschriebenen Basisklassenmethoden vorhanden sind, kann die Methode, die tatsächlich von einer solchen Referenz oder einem solchen Zeiger aufgerufen wird "spät" (d. H. Nach dem Laufzeitsystem der Sprache) wird nach dem tatsächlichen Typ des Objekts erwähnt.
Virtuelle Funktionen werden "spät" gelöst. Wenn die betreffende Funktion in der Basisklasse "virtuell" ist, wird die Implementierung der Funktion durch die am meisten abgeleitete Klasse gemäß dem tatsächlichen Typ des Objekts aufgerufen, auf den deklarierte Art des Zeigers oder der Referenz genannt wird. Wenn es nicht "virtuell" ist, wird die Methode "früh" aufgelöst und gemäß dem deklarierten Typ des Zeigers oder der Referenz ausgewählt.
Virtuelle Funktionen ermöglichen es einem Programm, Methoden aufzurufen, die nicht unbedingt einmal vorhanden sind, in dem der Code zusammengestellt wird.
In C ++, Virtuelle Methoden werden deklariert durch Vorbereitung der virtual
Schlüsselwort zur Deklaration der Funktion in der Basisklasse. Dieser Modifikator wird durch alle Implementierungen dieser Methode in abgeleiteten Klassen vererbt, was bedeutet, dass sie sich weiterhin gegenseitig übertrieben und verspätet sein können. Und selbst wenn Methoden der Basisklasse die virtuelle Methode aufrufen, werden sie stattdessen die abgeleitete Methode aufrufen. Überlastung tritt auf, wenn zwei oder mehr Methoden in einer Klasse denselben Methodennamen, aber unterschiedliche Parameter haben. Überschreiben bedeutet zwei Methoden mit demselben Methodennamen und denselben Parametern. Überladen wird auch als Funktionsübereinstimmung bezeichnet und als dynamische Funktionszuordnung überschrieben.
Beispiel
C ++
Zum Beispiel eine Basisklasse Tier
könnte eine virtuelle Funktion haben Essen
. Unterklasse Lama
würde implementieren Essen
anders als Unterklasse Wolf
, aber man kann aufrufen Essen
auf jeder Klasseninstanz, die als Tier bezeichnet wird, und erhalten Sie die Essen
Verhalten der spezifischen Unterklasse.
Klasse Tier { Öffentlichkeit: // Absichtlich nicht virtuell: Leere Bewegen() { std::Cout << "Dieses Tier bewegt sich in irgendeiner Weise" << std::Endl; } virtuell Leere Essen() = 0; }; // Die Klasse "Tier" kann eine Definition für das Essen besitzen, falls gewünscht. Klasse Lama : Öffentlichkeit Tier { Öffentlichkeit: // Die nicht virtuelle Funktionsbewegung wird vererbt, aber nicht überschrieben. Leere Essen() überschreiben { std::Cout << "Lamas essen Gras!" << std::Endl; } };
Dies ermöglicht es einem Programmierer, eine Liste von Objekten der Klasse zu verarbeiten Tier
, was wiederum zu essen ist (indem Sie anrufen Essen
) ohne wissen zu müssen, welche Art von Tier in der Liste sein kann, wie jedes Tier isst oder wie die vollständige Reihe möglicher Tiertypen aussehen könnte.
In C könnte der Mechanismus hinter virtuellen Funktionen auf folgende Weise bereitgestellt werden:
#enthalten / * Ein Objekt zeigt auf seine Klasse ... */ Struktur Tier { Const Struktur Tierklasse * Klasse; }; / * das enthält die virtuelle Funktion Animal.eat */ Struktur Tierklasse { Leere (*Essen) (Struktur Tier *); // 'virtuelle' Funktion }; /* Da Animal.move keine virtuelle Funktion ist Es ist nicht in der obigen Struktur. */ Leere Bewegen(Struktur Tier * selbst) { printf("<Animal bei %p> bewegte sich in irgendeiner Weise\n", (Leere *) selbst); } /* im Gegensatz zu Move, der Animal ausführt. Move direkt, Eat kann nicht wissen, welche Funktion (falls vorhanden) zum Kompilierzeit aufgerufen werden soll. Animal.eat kann nur zur Laufzeit gelöst werden, wenn das Essen aufgerufen wird. */ Leere Essen(Struktur Tier * selbst) { Const Struktur Tierklasse * Klasse = *(Const Leere **) selbst; wenn (Klasse->Essen) Klasse->Essen(selbst); // Animal.eat ausführen anders fprintf(Stderr, "Eat nicht implementiert\n"); } /* Implementierung von llama.eat Dies ist die Zielfunktion von 'void Eat (Struct Animal *) genannt werden. */ statisch Leere _Llama_eat(Struktur Tier * selbst) { printf("<Llama bei %p> Lama's Eat Grass!\n", (Leere *) selbst); } / * Klasse initialisieren */ Const Struktur Tierklasse Tier = {((Leere *) 0}; // Basisklasse implementiert Animal.eat nicht Const Struktur Tierklasse Lama = {_Llama_eat}; // Aber die abgeleitete Klasse tut es int hauptsächlich(Leere) { / * Init -Objekte als Instanz seiner Klasse */ Struktur Tier Tier = {& Tier}; Struktur Tier Lama = {& Lama}; Bewegen(& Tier); // Animal.Move Bewegen(& Lama); // llama.move Essen(& Tier); // Animal. Essen(& Lama); // löst llama.eat und führt aus }
Abstrakte Klassen und reine virtuelle Funktionen
A reine virtuelle Funktion oder Reine virtuelle Methode ist eine virtuelle Funktion, die von einer abgeleiteten Klasse implementiert werden muss, wenn die abgeleitete Klasse nicht ist abstrakt. Klassen, die reine virtuelle Methoden enthalten, werden als "abstrakt" bezeichnet und können nicht direkt instanziiert werden. EIN Unterklasse von einem abstrakte Klasse kann nur direkt so instanziiert werden, wenn alle vererbten reinen virtuellen Methoden von dieser Klasse oder einer übergeordneten Klasse implementiert wurden. Reine virtuelle Methoden haben typischerweise eine Erklärung (Unterschrift) und keine Definition (Implementierung).
Als Beispiel eine abstrakte Basisklasse Mathsymbol
kann eine reine virtuelle Funktion liefern Dooperation ()
, und abgeleitete Klassen Plus
und Minus
implementieren Dooperation ()
konkrete Implementierungen bereitstellen. Implementierung Dooperation ()
würde in der keinen Sinn ergeben Mathsymbol
Klasse, as Mathsymbol
ist ein abstraktes Konzept, dessen Verhalten ausschließlich für jede gegebene Art (Unterklasse) von definiert ist Mathsymbol
. In ähnlicher Weise eine bestimmte Unterklasse von Mathsymbol
wäre ohne eine Umsetzung von nicht vollständigDooperation ()
.
Obwohl reine virtuelle Methoden in der Klasse, die sie deklariert, typischerweise keine Implementierung haben , gegebenenfalls.[5][6]
Reine virtuelle Funktionen können auch verwendet werden, wenn die Methodendeklarationen verwendet werden, um eine zu definieren Schnittstelle - Ähnlich wie das Schlüsselwort der Schnittstelle in Java explizit angibt. In einer solchen Verwendung liefern abgeleitete Klassen alle Implementierungen. In solch einem DesignmusterDie abstrakte Klasse, die als Schnittstelle dient nur Reine virtuelle Funktionen, aber keine Datenmitglieder oder gewöhnlichen Methoden. In C ++ funktioniert die Verwendung von rein abstrakten Klassen wie Schnittstellen, da C ++ unterstützt Mehrfacherbe. Da viele OOP -Sprachen jedoch keine Mehrfachvererbung unterstützen, bieten sie häufig einen separaten Schnittstellenmechanismus. Ein Beispiel ist das Java -Programmiersprache.
Verhalten während der Konstruktion und Zerstörung
Sprachen unterscheiden sich in ihrem Verhalten, während die Konstrukteur oder Zerstörer eines Objekts läuft. Aus diesem Grund ist es im Allgemeinen entmutigt, virtuelle Funktionen bei Konstruktoren zu rufen.
In C ++ wird die Funktion "Basis" aufgerufen. Insbesondere wird die am stärksten abgeleitete Funktion, die nicht mehr abgeleitet ist als der aktuelle Konstruktor oder die Klasse des Destruktors, genannt.[7]: §15.7.3[8][9] Wenn diese Funktion eine reine virtuelle Funktion ist, dann dann undefiniertes Verhalten tritt ein.[7]: §13.4.6[8] Dies gilt auch dann, wenn die Klasse eine Implementierung für diese reine virtuelle Funktion enthält, da ein Aufruf einer reinen virtuellen Funktion explizit qualifiziert werden muss.[10] Eine konforme C ++ - Implementierung ist nicht erforderlich (und im Allgemeinen nicht in der Lage), indirekte Aufrufe an reine virtuelle Funktionen bei zu erkennen Zeit kompilieren oder Linkzeit. Etwas Laufzeitsysteme Ausgibt einen reinen virtuellen Funktionsanruffehler bei der Begegnung mit einem Aufruf zu einer reinen virtuellen Funktion bei Laufzeit.
In Java und C#wird die abgeleitete Implementierung aufgerufen, einige Felder werden jedoch noch nicht vom abgeleiteten Konstruktor initialisiert (obwohl sie auf ihre Standardnullwerte initialisiert werden).[11] Etwas Designmuster, so wie die Abstraktes FabrikmusterFördern Sie diese Verwendung in Sprachen aktiv, die diese Fähigkeit unterstützen.
Virtuelle Zerstörer
Objektorientierte Sprachen verwalten normalerweise die Speicherzuweisung und die Entschichtung automatisch, wenn Objekte erstellt und zerstört werden. Einige objektorientierte Sprachen ermöglichen jedoch, dass eine benutzerdefinierte Destruktor-Methode implementiert wird, falls gewünscht. Wenn die betreffende Sprache automatische Speicherverwaltung verwendet, ist der benannte benannte Destruktor (allgemein als Finalizer bezeichnet), der als für das betreffende Objekt bezeichnet wird, mit Sicherheit der geeignete. Wenn beispielsweise ein Objekt vom Typ Wolf, der Tier erbt, erstellt wird und beide benutzerdefinierten Zerstörer haben, wird der genannte, der in Wolf deklariert ist.
In den Kontexten des manuellen Gedächtnismanagements kann die Situation komplexer sein, insbesondere in Bezug auf statischer Versand. Wenn ein Objekt vom Typ Wolf erzeugt wird, aber von einem Tierzeiger hingewiesen wird und dieser tierische Zeigertyp gelöscht wird, kann der Destruktor, den man bezeichnet hat . Dies ist insbesondere bei C ++ der Fall, bei dem das Verhalten eine häufige Quelle für Programmierfehler ist, wenn Zerstörer nicht virtuell sind.
Siehe auch
- Zusammenfassung Methode
- Nachlass
- Superklasse
- Virtuelle Vererbung
- Virtuelle Klasse
- Schnittstelle (objektorientierte Programmierung)
- Komponentenobjektmodell
- Virtuelle Methode Tabelle
Verweise
- ^ "Polymorphismus (die Java ™ -Tutorials> Lernen der Java -Sprache> Schnittstellen und Vererbung)". docs.oracle.com. Abgerufen 2020-07-11.
- ^ "9. Klassen - Python 3.9.2 Dokumentation". docs.python.org. Abgerufen 2021-02-23.
- ^ "Schreiben von endgültigen Klassen und Methoden (die Java ™ -Tutorials> Lernen der Java -Sprache> Schnittstellen und Vererbung)". docs.oracle.com. Abgerufen 2020-07-11.
- ^ "PHP: Finales Schlüsselwort - Handbuch". www.php.net. Abgerufen 2020-07-11.
- ^ Reine virtuelle Destruktoren - cppreference.com
- ^ "ABC - Abstrakte Basisklassen: @ABC.ABSTRACTMETHOD"
- ^ a b "N4659: Arbeitsentwurf, Standard für die Programmiersprache C ++" (PDF).
- ^ a b Chen, Raymond (28. April 2004). "Was ist __purecall?".
- ^ Meyers, Scott (6. Juni 2005). "Rufen Sie niemals virtuelle Funktionen während der Konstruktion oder Zerstörung an.".
- ^ Chen, Raymond (11. Oktober 2013). "C ++ Eckfall: Sie können reine virtuelle Funktionen in der Basisklasse implementieren".
- ^ Ganesh, S.G. (1. August 2011). "Freude am Programmieren: Virtuelle Funktionen von Konstruktoren aufrufen".