Wie weiß die C ++ Compiler, die Implementierung einer virtuellen Funktion zu nennen?

StackOverflow https://stackoverflow.com/questions/203126

  •  03-07-2019
  •  | 
  •  

Frage

Hier ist ein Beispiel für Polymorphismus von http://www.cplusplus.com/doc /tutorial/polymorphism.html (zur besseren Lesbarkeit bearbeitet):

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
    protected:
        int width;
        int height;
    public:
        void set_values(int a, int b) { width = a; height = b; }
        virtual int area(void) =0;
};

class Rectangle: public Polygon {
    public:
        int area(void) { return width * height; }
};

class Triangle: public Polygon {
    public:
        int area(void) { return width * height / 2; }
};

int main () {
    Rectangle rect;
    Triangle trgl;
    Polygon * ppoly1 = &rect;
    Polygon * ppoly2 = &trgl;
    ppoly1->set_values (4,5);
    ppoly2->set_values (4,5);
    cout << ppoly1->area() << endl; // outputs 20
    cout << ppoly2->area() << endl; // outputs 10
    return 0;
}

Meine Frage ist, wie weiß der Compiler, dass ppoly1 ein Rechteck ist und dass ppoly2 ist ein Dreieck, so dass sie den richtigen Bereich () Funktion aufrufen kann? Es könnte, dass durch einen Blick auf das herauszufinden „Polygon * ppoly1 = ▭“ Linie und zu wissen, dass rect ist ein Rechteck, aber das wäre nicht in allen Fällen funktioniert, wäre es? Was ist, wenn Sie so etwas wie das getan?

cout << ((Polygon *)0x12345678)->area() << endl;

Unter der Annahme, dass Sie erlaubt, dass zufälligen Speicherbereich zugreifen zu können.

Ich würde dies testen, aber ich kann ich bin auf dem im Moment nicht auf dem Computer.

(Ich hoffe, ich bin fehlt nicht etwas offensichtlich ...)

War es hilfreich?

Lösung

Jedes Objekt (das mit mindestens einer virtuellen Funktion gehört zu einer Klasse) hat einen Zeiger, eine so genannten vptr. Er zeigt auf die vtbl seiner tatsächlichen Klasse (die jede Klasse mit virtuellen Funktionen hat mindestens eine der, möglicherweise mehr als eine für einige Mehrfachvererbung Szenarien).

Die vtbl enthält eine Reihe von Zeigern, eine für jede virtuelle Funktion. So zur Laufzeit verwendet der Code nur das vptr Objekt die vtbl ausfindig zu machen, und von dort die Adresse der aktuellen überschrieben Funktion.

In Ihrem speziellen Fall Polygon, Rectangle und Triangle jeder hat eine vtbl, die jeweils mit einem Eintrag, der auf seine relevanten area Methode. Ihre ppoly1 werden eine vptr zeigt auf Rectangle der vtbl und ppoly2 in ähnlicher Weise mit Triangle des vtbl. Hoffe, das hilft!

Andere Tipps

Chris Jester-Young gibt die grundlegende Antwort auf diese Frage.

Wikipedia hat eine mehr in die Tiefe der Behandlung.

Wenn Sie die vollständigen Details, wie diese Art der Sache funktioniert (und für alle Arten von Vererbung, einschließlich mehrere und virtueller Vererbung) wissen wollen, ist eine der besten Ressourcen ist Stan Lippmans „ Innerhalb des Objektmodells ++ C“.

Ohne Berücksichtigung Aspekte von Bindung, es ist nicht wirklich die Compiler, dass dies bestimmt.

Es ist die C ++ Laufzeit, der über vtables und vpointers, was das abgeleitete Objekt tatsächlich zur Laufzeit ausgewertet wird.

Ich empfehle Scott Meyers Buch Effective C ++ sehr für gute Beschreibungen, wie das gemacht wird.

Auch wird beschrieben, wie Standardparameter in einem Verfahren in einer abgeleiteten Klasse werden ignoriert und alle Standardparameter in einer Basisklasse sind noch genommen! Das ist verbindlich.

Um den zweiten Teil Ihrer Frage zu beantworten: die Adresse wahrscheinlich nicht eine V-Tabelle an der richtigen Stelle hat, und Wahnsinn wird folgen. Außerdem ist es nicht definiert nach der Norm.

cout << ((Polygon *)0x12345678)->area() << endl;

Dieser Code ist eine Katastrophe warten geschehen. Der Compiler wird es alles in Ordnung kompilieren, aber wenn es an der Zeit laufen kommt, werden Sie nicht auf eine gültige V-Tabelle zeigen werden, und wenn Sie Glück haben, wird das Programm nur zum Absturz bringen.

In C ++, sollten Sie nicht alt verwenden C-Stil wie dies wirft, sollten Sie dynamic_cast wie folgt:

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area();
ASSERT(obj != NULL);

cout << obj->area() << endl;

dynamic_cast wird NULL zurückgeben, wenn der angegebene Zeiger keine gültige Polygon ist Gegenstand so wird sie von der ASSERT gefangen werden.

Virtuelle Funktionstabellen. Nämlich, die beide Ihre Polygon-derived Objekte haben eine virtuelle Funktionstabelle, die Funktionszeiger auf die Implementierungen aller ihrer (nicht statisch) Funktionen enthält; und wenn Sie instanziieren ein Dreieck, die virtuellen Funktionszeiger für den Bereich () Funktion zeigt auf das Dreieck :: Bereich () Funktion; wenn Sie ein Rechteck instanziiert, die Fläche () Funktion zeigt auf das Rechteck :: Bereich () Funktion. Da virtuelle Funktionszeiger sind mit den Daten für ein Objekt im Speicher abgelegt entlang Bezugs jedes Mal, wenn das Objekt als Polygon, den entsprechenden Bereich () für das Objekt verwendet wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top