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.
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%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.
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 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).
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.
Tags und Links c++ language-lawyer linker-errors virtual-method