2013-12-13 4 views
5

Laut C++ 03 12.4/12, wenn ein destructorWas sagt C++ 03 12.4/12 über den Aufruf eines Basisklassen-Destruktors explizit über den Zeiger?

explizit aufgerufen wird, wenn das Objekt nicht den aus dem Klassentyp Destruktor abgeleiteten Klasse-Typs und nicht von einer Klasse destructor ist, hat das Programmverhalten nicht definiert

so habe ich diesen Code:

class Base {}; 
class Derived : public Base {}; 

char memory[100]; 
new(memory) Derived(); 
Base* ptr = (Base*)memory; 
ptr->~Base(); 

Hier wird das Objekt vom Typ Derived und "der Klassentyp des destructor" ist Base und so ist es l Ooks wie nach der Standard-Formulierung gibt es keinen Grund für UB.

Also gibt der obige Code UB nach dem Standard?

+0

hergeleitet abgeleitet in der Regel einige Mitglieder? – user1810087

+0

@itwasntpete: Nun, nicht unbedingt. Sie könnten beispielsweise eine neue Ausnahmeklasse erstellen, die separat erfasst werden kann, ohne neue Mitglieder hinzuzufügen. – sharptooth

+0

Ich habe eine Ahnung, dass dies UB ist, weil 12.4/12 die Form 'derivedPtr-> ~ Base()' mit der Absicht erlaubt, virtuelle Destruktoren zu unterstützen. C++ 03 ist hier jedoch nicht verfügbar. – MSalters

Antwort

4

Korrekt, es gibt kein undefiniertes Verhalten.

Dagegen gibt es Potenzial UB in diesem Fall abhängig von den Typen beteiligt: ​​

Base *ptr = new Derived(); 
delete ptr; 

Der Grund dafür ist, dass eine Anpassung für einige Typen durch die Umsetzung Derived*-Base* zu erhalten angewandt worden sein könnte. Ohne den Zeiger auf das vollständige Objekt gibt es keine Möglichkeit, die Speicherzuordnung korrekt freizugeben. Ein virtueller Destruktor stellt sicher, dass das Unterobjekt Base genügend Informationen für die Implementierung bereitstellt, um das wiederherzustellen (der Mechanismus des virtuellen Aufrufs muss in der Lage sein, den Zeiger Derived* wiederherzustellen, um ihn als this zu übergeben).

Aber in Ihrem Beispiel ist der Speicher nicht freigegeben und so gibt es keine Motivation, es UB zu machen. Natürlich ist es immer noch eine schlechte Idee, da das Derived Objekt konzeptionell in einem defekten Zustand ist. Sie haben keine legitime Möglichkeit, ~Derived, sogar anzurufen. In Ihrem Beispiel obwohl beide Arten trivial zerstörbar sind, so gibt es keine benötigen, um den Destruktor von beiden zu nennen.

+1

Emm ... Anpassung?'Derived' kann leicht' operator new() 'auf einem anderen Heap implementiert haben und das würde selbst ohne Anpassung schwerwiegende Konsequenzen haben. – sharptooth

+0

@sharptooth: ja, guter Punkt. Wenn 'new' und' delete' für diese Typen nicht überladen sind, dann ist es immer noch UB aus meinem Grund. –

+0

Eigentlich ist das new-delete Beispiel sowieso UB. Ihr Beispiel und mein Beispiel sind nur Beispiele dafür, was passieren kann. – sharptooth

0

Bitte korrigieren Sie mich, wenn ich falsch liege, ich denke, es gibt kein undefiniertes Verhalten, aber sollte immer noch im Sinne der Menschheit (oder Wartbarkeit) vermieden werden. aber betrachte Derived schafft eine Art von Mitglied, z.B. ein geteilter Zeiger (was selbst für Ausnahmen nicht untypisch ist :). Ich habe versucht, diesen Code auf meinem Rechner und auch auf codepad:

class Base { 
public: 
    boost::shared_ptr<int> x; 
}; 
class Derived : public Base { 
public: 
    boost::shared_ptr<int> y; 
}; 


int main(int argc, char *argv[]) { 
    boost::shared_ptr<int> xx(new int(0xff)); 
    boost::shared_ptr<int> yy(new int(0xaa)); 

    int memory[100]; 
    for(int i=0; i<100; i++) 
    memory[i] = 0; 
    Derived* foo = new(memory) Derived(); 
    foo->x = xx; 
    foo->y = yy; 
    (*foo->y)--; 
    Base* ptr = (Base*)foo; 

    ptr->~Base(); 

    Derived* bar = new(memory) Derived(); 

    bar->x = xx; 
    bar->y = yy; 

    foo->~Derived(); 

    return 0; 
} 

hier ist, dass die Shared_ptr yy nicht freigegeben wird, und niemand konnte garantieren, niemals über Abgeleitet sollte nicht vergessen, jede Art von Material zur Verfügung stellen.

+1

Etwas nicht freigegeben bedeutet nicht UB. – sharptooth

+0

In diesem Fall denke ich *, dass die UB ein Objekt über dem Speicher konstruiert, der von einem nicht-trivial zerstörbaren Objekt besetzt ist, das nicht zerstört wurde. Also denke ich * das UB ist der zweite Aufruf von 'new (memory) Derived()', der über das 'y'-Mitglied scrollt. Aber selbst wenn ich falsch liege und das ist nicht UB, dann ist bar-> y 'null, also ist Dereferenzierung es. [Diese Bemerkung ist auf dem Codepad-Code, ich habe gerade bemerkt, dass der Code hier anders ist.] –

1

Es ist nicht UB, da beide Klassen trivial Destruktoren haben, und die destructor hat fordern daher die gleiche Wirkung wie nicht die destructor Aufruf (und nicht ruft der Destruktor UB ist sicherlich nicht).

+0

Trifft das auch zu? Base * base = new Derived(); Base löschen; – sharptooth

+0

@sharptooth: Ich würde so weit gehen, ja zu sagen; in der Tat würde ich denken: Base Base; static_cast (& base) -> ~ Derived(); 'sollte auch funktionieren (so absurd wie es klingt), weil beide triviale Destruktoren haben, was bedeutet, dass ihre Destruktoren keinen Effekt haben müssen ... – Mehrdad

+0

Der Text vom Standard, der ist in der Frage zitiert hat keine Ausnahme für triviale Destruktoren. Diese Antwort bezieht sich nicht auf undefiniertes Verhalten, sondern auf mögliche Konsequenzen des Codes. –

Verwandte Themen