Warum kann die virtuelle Funktion bei einer Zuweisung mit "neu" nicht nicht implementiert werden?

8
%Vor%

Für Objekte auf dem Stapel funktioniert . Aber für die Zuweisung auf dem Heap mit new (nicht malloc ) gibt es einen Linker-Fehler:

%Vor%     
iammilind 03.06.2011, 17:12
quelle

5 Antworten

6

Erstens ist dieser Code nicht kompilierbar, da in C ++ void * nicht implizit in A * konvertiert werden kann. Eine explizite Umwandlung ist erforderlich.

Zweitens ist das Beispiel mit malloc völlig irrelevant. malloc ordnet rohen Speicher zu, der absolut keine Beziehung zu bestimmten Typen hat. In diesem Fall weiß malloc über A und erstellt kein Objekt vom Typ A .

Aus diesem Grund sollte das reale Beispiel für diese Frage wie folgt aussehen

%Vor%

Und die Frage ist, warum die erste Deklaration keinen Fehler erzeugt, während die zweite dies tut.

Aus formeller Sicht ist Ihr Programm ungültig, weil es die formellen Anforderungen der Sprache C ++ (speziell ODR) verletzt. In der Praxis könnten oder sollten beide Deklarationen den gleichen Fehler erzeugen, da das Objekt in beiden Fällen formal einen Zeiger auf VMT benötigt. In diesem Fall kann VMT nicht erstellt werden, da einige Funktionen nicht definiert sind. Die erste Deklaration geht jedoch einfach durch, nur weil der Compiler alle Verweise auf VMT für die erste Deklaration (und nicht für die zweite Deklaration) optimieren konnte. Es ist auch durchaus möglich, dass der Compiler das gesamte obj -Objekt optimieren konnte, da es sonst nirgendwo referenziert wird.

In GCC (da Sie scheinen, GCC zu verwenden) ist es leicht, den gleichen Fehler für die erste Deklaration als auch

auszulösen %Vor%

Der obige Code erzeugt den gleichen Linker-Fehler in GCC, obwohl die undefinierte Funktion foo immer noch nicht in diesem Code verwendet wird.

Mit anderen Worten, es ist einfach eine Frage des Hinzufügens einer ausreichenden Menge an Code, um den Compiler glauben zu machen, dass die VMT des Objekts benötigt wird. Der Unterschied im Verhalten zwischen den Deklarationen hat in diesem Fall nichts mit der Sprache C ++ zu tun. Es ist nur ein Implementierungsproblem für Ihren Compiler.

    
AnT 03.06.2011, 17:41
quelle
9

Weil malloc den Konstruktor von A nicht aufruft (oder versucht, in diesem Fall aufzurufen), während new das tut.

Dieser Code kompiliert und notiert, wo Linkfehler mit GCC auftreten:

%Vor%     
Neil Butterworth 03.06.2011 17:13
quelle
3

Sie können eine virtuelle Funktion nicht unimplementiert lassen, auch wenn sie nicht verwendet wird (weil sie tatsächlich von der vtable verwendet wird). Dies ist der Fehler im Code.

Der Fehler manifestiert sich in dieser speziellen Art und Weise aufgrund einer speziellen Implementierung von vtables im Compiler. Sie haben die erste virtuelle Funktion nicht implementiert. Der Compiler fügt die Vtable immer dann ein, wenn sie die Implementierung der ersten virtuellen Funktion der Klasse sieht. Da es keine gibt, gibt es keine vtable.

Wenn Sie die zweite Funktion nicht implementiert lassen, wird der Linker über diese spezifische Funktion und nicht über die vtable beschweren.

[Bearbeiten] Dein Compiler hat wahrscheinlich eine Kopie von A auf dem Stack optimiert, deshalb hat sich der Linker nicht beschweren.

Die malloc -Zeile referenziert nicht wirklich auf ein Objekt vom Typ A, deshalb erzeugt es kein Linker-Problem. Es gibt jedoch ein anderes Problem mit dieser Zeile: Es sollte nicht kompiliert werden. malloc gibt void* zurück, das nicht in andere Arten von Zeigern umgewandelt wird, ohne dass eine Umwandlung erfolgt.

    
n.m. 03.06.2011 17:23
quelle
1

Der Standard erfordert genau eine Implementierung von A::foo , wenn A ist überall im Programm instanziiert. Unabhängig davon, ob die Instanziierung erfolgt durch Deklaration einer lokalen Variablen oder durch ein neuer Ausdruck. Bei dieser Regel ist jedoch keine Diagnose erforderlich gebrochen; wenn Sie keine Erklärung abgeben oder wenn Sie zwei oder mehr angeben, es ist einfach undefiniertes Verhalten. Alles was der Compiler macht ist "richtig". In diesem Fall ist wahrscheinlich Folgendes passiert:

  • Der Grund, warum die Definition erforderlich ist, ist, dass auf sie in der vtable
  • verwiesen wird
  • Der Konstruktor von A ist inline, daher ist der Code, der den vptr initialisiert (und die Instanziierung der vtable auslöst), für den Compiler vollständig sichtbar,

  • Da alle Verwendungen des Objekts für den Compiler sichtbar sind, kann es sehen, dass das vptr nie benutzt wird, also unterdrückt es es einfach.

  • und ohne vptr muss keine vtable generiert werden, daher gibt es keinen Verweis auf die virtuelle Funktion.

In der Summe hängt es davon ab, wie der Compiler optimiert; Sie könnten einen Fehler erhalten sowohl für die lokale Erklärung und den neuen Ausdruck, oder für beide, oder für den einen und nicht für den anderen. Und es könnte von der Optimierung abhängen Optionen oder was auch immer. Was C ++ betrifft, könnte es davon abhängen die Phasen des Mondes, und anstatt eines Fehlers, könnten Sie einfach bekommen Code, der abgestürzt ist, als Sie es ausgeführt haben (aber die Szenarien, die ich zuerst genannt habe, sind am wahrscheinlichsten).

    
James Kanze 03.06.2011 18:26
quelle
0

Ungenutztes ist irrelevant. Definieren Sie alle virtuellen Funktionen. So einfach ist das.

Ihr Objekt für die automatische Speicherdauer (das Sie ein Objekt "auf dem Stapel" genannt haben) wird nicht [polymorph] verwendet, so dass Sie keine Diagnose erhalten. Das macht es nicht richtig.

    
quelle