Wenn Klassen mit virtuellen Funktionen mit vtables umgesetzt werden, wie eine Klasse ohne virtuelle Funktionen implementiert?

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

  •  01-07-2019
  •  | 
  •  

Frage

Insbesondere wäre nicht da hat ohnehin eine Art Funktionszeiger an seinem Platz sein?

War es hilfreich?

Lösung

Nicht virtuelle Member-Funktionen sind wirklich nur ein syntaktischer Zucker, wie sie fast wie eine normale Funktion, aber mit Zugriffsprüfung und einem impliziten Objektparameter.

struct A 
{
  void foo ();
  void bar () const;
};

ist im Grunde das gleiche wie:

struct A 
{
};

void foo (A * this);
void bar (A const * this);

Die V-Tabelle ist so erforderlich, dass wir die richtige Funktion für unsere spezifischen Objektinstanz aufrufen. Zum Beispiel, wenn wir haben:

struct A 
{
  virtual void foo ();
};

Die Implementierung von 'foo' könnte etwas annähern wie:

void foo (A * this) {
  void (*realFoo)(A *) = lookupVtable (this->vtable, "foo");
  (realFoo)(this);   // Make the call to the most derived version of 'foo'
}

Andere Tipps

Ich denke, dass der Ausdruck " Klassen mit virtuellen Funktionen mit vtables umgesetzt werden " ist irreführend, Sie.

Der Satz macht es wie Klassen mit virtuellen Funktionen klingen implementiert " in Art und Weise A " und Klassen ohne virtuelle Funktionen sind implementiert " in Art und Weise B ".

In der Realität Klassen mit virtuellen Funktionen, neben umgesetzt als Klassen sind, sie haben auch eine VTable. Ein anderer Weg, um es zu sehen ist, dass „‚vtables‘implementieren, um den‚virtuelle Funktion‘Teil einer Klasse“.

Weitere Informationen darüber, wie sie beide arbeiten:

Alle Klassen (mit virtuellen oder nicht-virtuellen Methoden) sind structs. Der nur Unterschied zwischen einer Struktur und einer Klasse in C ++ ist, dass standardmäßig die Mitglieder in structs und privaten in Klassen öffentlich sind. Aus diesem Grund werde ich den Begriff Klasse hier verwenden, um sich sowohl auf Strukturen und Klassen. Denken Sie daran, sie sind fast Synonyme!

Daten Mitglieder

Klassen sind (wie structs sind) nur Blöcke von zusammenhängendem Speicher, wobei jedes Element in der Sequenz gespeichert ist. Beachten Sie, dass einige Male wird es Lücken zwischen den Mitgliedern für CPU architektonischen Gründen, so kann der Block größer sein als die Summe seiner Teile.

Methoden

Methoden oder „Elementfunktionen“ sind eine Illusion. In Wirklichkeit ist es nicht so etwas wie eine „Member-Funktion“. Eine Funktion ist immer nur eine Folge von Maschinencodeanweisungen irgendwo im Speicher abgelegt. Um einen Anruf zu machen, springt der Prozessor zu dieser Position von Speichern und beginnt mit der Ausführung. Man könnte sagen, dass alle Methoden und Funktionen sind ‚global‘ und jede Angabe des Gegenteils ist eine bequeme Illusion vom Compiler erzwungen.

Offensichtlich wirkt ein Verfahren, wie es auf ein bestimmtes Objekt gehört, so eindeutig ist mehr los. Um einen bestimmten Aufruf einer Methode (eine Funktion) auf ein bestimmtes Objekt zu binden, hat jedes Mitglied Verfahren ein verborgenes Argument, das ein Zeiger auf das Objekt in Frage. Das Mitglied ist versteckt , dass Sie nicht hinzufügen, es zu Ihrem C ++ Code selbst, aber es gibt nichts Magisches an sich - es ist sehr real. Wenn Sie sagen, dies:

void CMyThingy::DoSomething(int arg);
{
    // do something
}

Der Compiler wirklich tut dies:

void CMyThingy_DoSomething(CMyThingy* this, int arg)
{
    /do something
}

Schließlich, wenn Sie schreiben diese:

myObj.doSomething(aValue);

Der Compiler sagt:

CMyThingy_DoSomething(&myObj, aValue);

Keine Notwendigkeit für Funktionszeiger überall! Der Compiler weiß schon, welche Methode Sie anrufen, so dass es direkt aufruft.

Statische Methoden sind noch einfacher. Sie haben nicht eine diese Zeiger, so dass sie umgesetzt werden genau so, wie Sie sie schreiben.

Das ist ist! Der Rest ist nur bequem Syntax sugaring: Der Compiler weiß welche Klasse eine Methode gehört, so dass es sicher ist es nicht, dass Sie die Funktion ohne Angabe eines, das nicht nennen sich. Es nutzt auch dieses Wissen myItem übersetzt this->myItem, wenn es eindeutig ist, dies zu tun.

(ja, das ist richtig: Mitglied Zugang in einem Verfahren ist immer getan indirekt über einen Zeiger, auch wenn Sie nicht sehen, ein)

( Bearbeiten : Entfernte letzten Satz und geschrieben getrennt, so kann es separat kritisiert werden)

Die virtuellen Methoden sind erforderlich, wenn Sie Polymorphismus verwenden möchten. Der virtual Modifikator stellt die Methode in der VMT für die späte Bindung und dann zur Laufzeit wird entschieden, welche Methode, von der Klasse ausgeführt wird.

Wenn die Methode nicht virtuell ist -. Es bei der Kompilierung entschieden wird, aus der Klasseninstanz wird es ausgeführt werden

Funktionszeiger werden verwendet hauptsächlich für Rückrufe.

Wenn eine Klasse mit einer virtuellen Funktion mit einer V-Tabelle implementiert wird, dann eine Klasse ohne virtuelle Funktion ohne VTable implementiert wird.

A vtable enthält die Funktionszeiger benötigt, um einen Anruf zu dem entsprechenden Verfahren zu versenden. Wenn die Methode nicht virtuell ist, geht der Anruf an die bekannten Typen der Klasse, und keine indirection benötigt wird.

Für eine nicht-virtuelle Methode der Compiler einen Normalfunktionsaufruf generieren können (beispielsweise Anruf an eine bestimmte Adresse mit diesem Zeiger als Parameter übergeben wird) oder sogar Inline. Für eine virtuelle Funktion, wird der Compiler weiß nicht, in der Regel zum Zeitpunkt der Kompilierung, bei der Adresse um den Code aufrufen, daher erzeugt es Code, der die Adresse in der V-Tabelle zur Laufzeit sucht und ruft dann die Methode. Es stimmt, auch für virtuelle Funktionen kann der Compiler manchmal korrekt den richtigen Code zum Zeitpunkt der Kompilierung lösen (zum Beispiel Methoden auf lokale Variablen ohne Zeiger / Verweis aufgerufen).

(zog ich diesen Abschnitt von meiner ursprünglichen Antwort, so dass sie separat kritisiert werden können. Es ist eine Menge mehr prägnant und auf den Punkt Ihrer Frage, also in gewisser Weise ist es eine viel bessere Antwort)

Nein, es gibt keine Funktionszeiger; stattdessen der Compiler stellt sich das Problem inside-out .

Der Compiler ruft eine globale Funktion mit ein Zeiger auf das Objekt statt fordern einige Spitz zu innerhalb des Objekts funktionieren

Warum? Weil es in der Regel viel effizienter auf diese Weise. Indirekte Anrufe sind teuer Anweisungen.

Es gibt keine Notwendigkeit für Funktionszeiger, wie es während der Laufzeit nicht ändern kann.

Branchen werden direkt an den kompilierten Code für die Methoden erzeugt werden; wie wenn Sie Funktionen, die überhaupt nicht in einer Klasse sind, Zweige erzeugen gerade zu ihnen.

Der Compiler / Linker Links direkt die Methoden aufgerufen werden. Keine Notwendigkeit für eine VTable indirection. BTW, was hat das mit "Stack vs. Halde" zu tun?

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