2013-07-08 17 views
6

Ich lief in einen Haufen Korruption heute durch verschiedene CRT-Einstellungen (MTd MDD) in meiner DLL und mein eigentliches Projekt verursacht. Was ich seltsam fand, ist, dass die Anwendung nur abgestürzt ist, wenn ich den Destruktor in der DLL als virtuell einstelle. Gibt es eine einfache Erklärung dafür? Ich bekomme, dass ich Speicher nicht freigeben kann, der nicht auf meinem Heap ist, aber wo genau ist der Unterschied, wenn ich den Destruktor als nicht virtuell definiere.CRT virtuellen Destruktor

nur einige Code es ein wenig klarer

Die DLL sicher sein

#pragma once 
class CTestClass 
{ 
public: 
    _declspec(dllexport) CTestClass() {}; 
    _declspec(dllexport) virtual ~CTestClass() {}; 
}; 

Und mein Projekt

int main(int argc, char* argv[]) 
{ 
    CTestClass *foo = new CTestClass; 
    delete foo; // Crashes if the destructor is virtual but works if it's not 
} 
+0

ALSO, haben Sie das gleiche Problem, indem Sie die declspec in die * class * ('class _declspec (dllexport) CTestClass {...}') verschieben und die pro-member declspecs entfernen? Nur neugierig. Beachten Sie, dass der aufrufende Code und die DLL dieselbe CRT verwenden sollten (debuggen oder release), also ist das etwas zu beachten. Ich bin mir nicht einmal sicher, dass Mixed-Modi unterstützt werden (glaube ich nicht). – WhozCraig

+6

Sie haben mehrere Kopien des CRT in Ihrem Prozess. Und Sie exportieren nur die Klassenmethoden, nicht die V-Tabelle. Der Versuch, herauszufinden, wie das alles interagiert, um deinen Code zu bombardieren, ist nicht so produktiv, wird erwartet.Beim Exportieren einer Klasse mit virtuellen Methoden müssen Sie die gesamte Klasse exportieren. Stellen Sie __declspec (dllexport) neben das Schlüsselwort * class *. Und Sie müssen sicherstellen, dass ein einzelner Zuordner verwendet wird, um das Objekt zu erstellen und zu zerstören. Sehr schwer zu garantieren, es sei denn, Sie erstellen mit/MD konsistent und verwenden die exakt gleiche Compiler-Version. Die Offenlegung von C++ - Klassen über Modulgrenzen hinweg ist nur riskant. –

+0

Sie haben wahrscheinlich Recht, auch wenn ich herausfinden, warum es nicht funktioniert, wird es mir nicht zu viel helfen. Danke trotzdem für deine Gedanken :) – Poisonbox

Antwort

2

Es gibt einen Unterschied zwischen

class CTestClass 
{ 
public: 
    _declspec(dllexport) CTestClass() {} 
    _declspec(dllexport) virtual ~CTestClass() {} 
}; 

und

__declspec(dllexport) class CTestClass 
{ 
public: 
    CTestClass() {} 
    virtual ~CTestClass() {} 
}; 

Im ersteren Fall, dass Sie einen Compiler angewiesen, nur zwei Elementfunktionen zu exportieren: CTestClass :: CTestClass() und CTestClass :: ~ CTestClass(). Aber im letzteren Fall würden Sie einen Compiler anweisen, die Tabelle der virtuellen Funktionen ebenfalls zu exportieren. Diese Tabelle wird benötigt, sobald Sie einen virtuellen Destruktor haben. Es könnte also die Ursache für den Absturz sein. Wenn Ihr Programm versucht, den virtuellen Destruktor aufzurufen, sucht es in der zugehörigen virtuellen Funktionstabelle nach, aber es ist nicht richtig initialisiert, so dass wir nicht wissen, wohin es dann wirklich zeigt. Wenn Ihr Destruktor nicht virtuell ist, benötigen Sie keine virtuelle Funktionstabelle und alles funktioniert einwandfrei.

0

Du hast nicht wirklich Post genug Code zu machen. Aber Ihr Beispiel sollte nicht abstürzen, weil es nichts falsch mit ihm:

int main(int argc, char* argv[]) 
{ 
    // 1. Allocated an instance of this class in *this/exe* heap, not the DLL's heap 
    // if the constructor allocates memory it will be allocated from the DLL's heap 
    CTestClass *foo = new CTestClass; 

    // 2. Call the destructor, if it calls delete on anything it will be freed from the DLL's heap since thats where the destructor is executing from. Finally we free the foo object from *this/exe* heap - no problems at all. 
    delete foo; 
} 

Ich vermute, dass in Ihrem realen Code, den Sie muss mit dem Operator auf ein Objekt löschen, wer Betreiber neu im Rahmen der dll ausgeführt wurde . Und ohne das virtuelle Schlüsselwort verpassen Sie wahrscheinlich den Destruktoraufruf, der den Kreuzkontext löscht.

+0

"Dein Beispiel sollte NICHT abstürzen, weil da nichts falsch ist", genau das dachte ich mir auch, aber ich habe den Code bis auf das, was oben geschrieben wurde, kaputt gemacht Scheitern. – Poisonbox

+0

Können Sie ein Beispielprojekt hochladen? Es muss etwas anderes schief gehen – paulm

0

virtueller Destruktor ist nur erforderlich, wenn Sie einen Vererbungshierarchiebaum haben. Das virtuelle Schlüsselwort stellt sicher, dass der Zeiger auf das tatsächliche Objekt (nicht den Typ des Objekts) zerstört wird, indem sein Destruktor in der Vtable gefunden wird. Da in diesem Beispiel CTestClass nicht nach einer anderen Klasse, sondern nach dem von Ihnen angegebenen Code erbt, handelt es sich in gewisser Weise um eine Basisklasse und benötigt daher keinen virtuellen Destruktor. Ich gehe davon aus, dass es möglicherweise eine andere unter der Motorhaube Implementierungsregel verursacht, aber Sie sollten nicht virtuelle mit Basisklassen verwenden. Jedes Mal, wenn Sie ein abgeleitetes Objekt erstellen, erstellen Sie auch seine Basis (aus polymorphem Grund) und die Basis wird immer zerstört (das abgeleitete wird nur zerstört, wenn Sie den Destruktor dafür virtuell machen und es daher in eine virtuelle Laufzeittabelle legen) .

Dank