0

Ich bin wirklich auf dieses Problem fest. Ich habe eine Anwendung mit einem Speicherleck. Um das Problem loszuwerden, muss ich Destruktoren für Klassen aufrufen.Aufruf von 3 Destruktor-Schichten in Polymorphie

für 2 Schichten von Klassen, dieses Problem ist trivial, aber in dieser Situation gibt es drei Schichten von Klassen und ich kann keinen Weg finden, um diese zu kompilieren zu erhalten:

#include<iostream> 

using namespace std; 

/* *MUST* be an abstract class and contains data that must be freed */ 
class C { 
public: 
    virtual ~C()=0; 
}; 

/* Contains concrete methods and contains data that has to be freed */ 
class B:public C { 
public: 
    virtual ~B(); 
}; 

/* contains no data, only methods nothing needs to be freed in here */ 
class A:public B { 
public: 
    ~A(); 
}; 

int main(){ 
    C *c = new A; 
    return 0; 
} 

Wenn ich den Code, den ich kompilieren erhalten Sie den Fehler:

[email protected] ~ $ gcc deleteme.cpp -std=c++11 
/tmp/cc7nxaw5.o: In function `main': 
deleteme.cpp:(.text+0x12): undefined reference to `operator new(unsigned int)' 
/tmp/cc7nxaw5.o: In function `__static_initialization_and_destruction_0(int, int)': 
deleteme.cpp:(.text+0x4b): undefined reference to `std::ios_base::Init::Init()' 
deleteme.cpp:(.text+0x62): undefined reference to `std::ios_base::Init::~Init()' 
/tmp/cc7nxaw5.o: In function `B::B()': 
deleteme.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0x16): undefined reference to `vtable for B' 
/tmp/cc7nxaw5.o: In function `A::A()': 
deleteme.cpp:(.text._ZN1AC2Ev[_ZN1AC5Ev]+0x16): undefined reference to `vtable for A' 
/tmp/cc7nxaw5.o:(.rodata._ZTV1C[_ZTV1C]+0x8): undefined reference to `__cxa_pure_virtual' 
/tmp/cc7nxaw5.o:(.rodata._ZTV1C[_ZTV1C]+0xc): undefined reference to `__cxa_pure_virtual' 
/tmp/cc7nxaw5.o:(.rodata._ZTI1C[_ZTI1C]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info' 
collect2: error: ld returned 1 exit status 

jemand eine Möglichkeit, herauszufinden, kann, dass ich die Destruktoren umschreiben kann, so dass Destruktoren für A, B und C sind alle aufgerufen?

Ich reiße meine Haare mit diesem, jede Hilfe würde geschätzt werden. Ich kann jedoch nicht die Anforderungen in Kommentaren oder der Vererbungshierarchie ändern, um dies zu tun, würde ich die gesamte Anwendung neu schreiben müssen.

Sehr geschätzt.

Edit: so vielen Dank für die Kommentare unten auf die Frage, die Lösung, die funktioniert für mich ist:

#include<iostream> 

using namespace std; 

/* *MUST* be an abstract class and contains data that must be freed */ 
class C { 
public: 
    virtual ~C()=0; 
}; 

C::~C(){ 
    cout<<"destroying C"<<endl; 
} 

/* Contains concrete methods and contains data that have to be freed */ 
class B:public C { 
public: 
    ~B(); 
}; 

B::~B(){ 
    cout<<"destroying B"<<endl; 
} 

/* contains no data, only methods nothing needs to be freed in here */ 
class A:public B { 
public: 
    ~A(); 
}; 

A::~A(){ 
    cout<<"destroying A"<<endl; 
} 

int main(){ 
    C *c = new A; 
    delete c; 
    return 0; 
} 

Vielen Dank für Ihre Ratschläge sehr.

+3

Geben Sie Ihre Destruktoren Definition und vielleicht wird es besser funktionieren. '~ B() {}' –

+1

Auch das Hinzufügen von 'delete c;' wird sie tatsächlich aufrufen. Oder besser: Verwenden Sie intelligente Zeiger. –

+1

Das hat mich verwirrt: '/ * * MUSS * eine abstrakte Klasse sein und enthält Daten, die befreit werden müssen * /' und 'virtual ~ C() = 0;'. 'C' hat Daten zu befreien. Nur "C" sollte genug über "C" wissen, um dies zu können. 'C' sollte einen Destruktor haben. Klärung. Sie können einen reinen virtuellen Destruktor haben und trotzdem einen Destruktor bereitstellen. Seltsam, aber wahr. – user4581301

Antwort

1

Die Lösung besteht darin, Destruktoren in den Klassen zu implementieren, die sie benötigen, und den Compiler den Rest übernehmen zu lassen.

Zunächst einmal hat C einige Daten, die befreit werden muss, so C eine destructor wie diese bekommt:

class C{ 
    int* cp; 

public: 

    C(){ cp = new int;} 

    virtual ~C(){ 
     delete cp; 
     cout << "~C()" << endl; 
    } 

    virtual void someMethod()=0; 
}; 

Klasse B sollte die rein virtuelle Funktion implementieren und Daten, die als befreit werden muss Nun, so:

Schließlich Klasse A bietet nur einige Methoden, so dass wir keinen Destruktor benötigen. Aber ich werde für die Demonstration eines auch hier sind:

class A: public B{ 
    public: 
    virtual ~A(){ 
     cout << "~A()" << endl; 
    } 
}; 

diese Klasse Constructing und ruft die destructor mit delete alle drei Destruktoren richtig nennen:

int main(){ 
    C *c = new A; 
    delete c; 
    return 0; 
} 

Ausgang:

~A() 
~B() 
~C() 

Try it online

Als ein Hinweis: Es l Wie im Code, den Sie gepostet haben, haben Sie versucht, die Deklaration eines reinen virtuellen Destruktors zu verwenden, um Ihre Klasse abstrakt zu machen. Aber wenn Sie Ressourcen in der Klasse freigeben müssen, müssen Sie eine andere Funktion für diesen Zweck verwenden, wenn Sie sowieso keine reine virtuelle Funktion haben.

Edit:
Lesen here man lernen kann, dass es tatsächlich gültig ist, einen rein Spezifizierer und eine Definition für die gleiche Funktion zur Verfügung zu stellen - die auch auf Destruktoren gilt.So kann man in der Tat dies tun:

class C{ 
    virtual ~C() = 0; 
}; 

C::~C(){ /*Implementation here*/} 

Am Ende, sobald mindestens eine Funktion ist rein virtuelle (=0) Klasse abstrakt ist. Wenn dies der Destruktor ist oder eine andere Funktion spielt keine Rolle. Aber wenn Ihre Klasse Ressourcen freigeben muss, benötigen Sie einen Körper für Ihren Destruktor.

+0

Vielen Dank für Ihre Antwort, eine der Fragen, die ich von diesem hatte, war, wenn Sie die virtuelle Vererbung der Destruktoren schichten könnte, sieht es so aus als ob Sie können. Vielen Dank :) Ich werde diese Lösungen mit der tatsächlichen Codebasis testen müssen, weil es entscheidend ist, dass C eine abstrakte Klasse ist, da es einen Vektor von polymorphen Klasseninstanzen gibt und ich eine Pandoras-Box von Problemen öffne, wenn C nicht abstrakt ist. – Owl

+0

In diesem Fall ist C abstrakt - es ist einfach nicht der Destruktor, der es abstrakt macht, sondern 'someMethod()' – Anedar

+0

Das funktioniert perfekt - auch es hat mein Speicherleck behoben. Vielen Dank. Ich habe jetzt ein Speicherleck-freies neurales Netzwerk! – Owl