2017-05-10 7 views
-1

Wenn ich versuche, das abgeleitete Objekt polymorph zu löschen (das heißt: Basisklasse public virtual destructor hat) immer noch, warum derived Klasse Privat destructor ist genannt zu werden? Warum funktioniert die Bereichsauflösung privat hier nicht.Privat Abgeleitet Destructor

class Base 
{ 
protected: 
    Base() { cout << "Base constructor.\n"; } 
public: 
    virtual ~Base() { cout << "Base destructor.\n"; } 
}; 

class Derived :public Base 
{ 
public: 
    Derived() { cout << "Derived constructor.\n"; } 
private: 
    ~Derived() { cout << "Derived destructor.\n"; } 
}; 

int main() 
{ 
    Base *p = new Derived(); 
    delete p; 
} 

Ausgang:

Base constructor. 
Derived constructor. 
Derived destructor. 
Base destructor. 
+1

Weil Sie abgeleiteten dtor durch Basisklassenzeiger aufrufen ... –

Antwort

3

Da Destruktoren in umgekehrter Reihenfolge der Konstruktoren aufgerufen werden und virtueller Destruktor immer aufgerufen wird.

private hat nichts zu tun, wenn eine virtuelle Funktion aufgerufen wird.

Wie ich hier darauf hingewiesen:

Why would a virtual function be private?

ISO C++ 1998 Norm ab heißt es ausdrücklich:

§10.3 [...] Zutrittskontrolle (Ziffer 11) nicht berücksichtigt in Überschreiben bestimmen.


Ein bisschen philosophisch offtopic:

geht weiter ist es das, was STL tut für iostreams: Definition von Non-Virtual Interface, dh alle öffentlichen Funktionen (mit Ausnahme von Destruktoren) ist nicht-virtuelle und alle virtuellen Funktionen sind entweder protected oder private. Öffentliche Funktionen rufen virtuelle geschützte oder private auf. Dies gibt einen sehr klaren Einstiegspunkt in die gesamte Hierarchie.

1

Es ist, aber Sie rufen nicht ~Derived() direkt. Wenn Sie

Derived *p = new Derived(); 
delete p; 

verwenden würden, dann würden Sie den Fehler erhalten. Wenn Sie jedoch indirekt über Polymorphismus auf ~Derived() zugreifen (z. B. durch Aufrufen von ~Base()), gilt der Zugriffsspezifizierer private nicht.

Nach [class.access.virt#1]:

Die Zugriffsregeln (Paragraf [class.access]) für eine virtuelle Funktion, die durch ihre Erklärung bestimmt werden und nicht durch die Regeln für eine Funktion betroffen, die es später außer Kraft setzt. [Beispiel:

class B { 
public: 
    virtual int f(); 
}; 

class D : public B { 
private: 
    int f(); 
}; 

void f() { 
    D d; 
    B* pb = &d; 
    D* pd = &d; 

    pb->f();      // OK: B​::​f() is public, D​::​f() is invoked 
    pd->f();      // error: D​::​f() is private 
} 

- Ende Beispiel]

Und wieder in footnote 111 in [class.virtual]:

Zutrittskontrolle wird bei der Bestimmung überwiegenden nicht berücksichtigt.

0

Sie verwenden einen virtuellen Destruktor, weil Sie möchten, dass alle Destruktoren in Ihrer Vererbung aufgerufen werden.Genau das, was du bekommst. Das private gilt nicht, da Sie den Destruktor nicht explizit aufrufen. Wenn Ihr Destruktor nicht virtuell ist, erhalten Sie nur Base::~Base() aufgerufen. Normalerweise ist das nicht, was Sie wollen, wenn Sie Polymorphismus haben.

0

Sie können den Destruktor über einen Zeiger auf B aufrufen, da es dort bereits öffentlich ist. Es ist der statische Typ des Zeigers, der in diesem Fall verwendet wird, um diesen Zugriff zu bestimmen.

[class.access.virt/1]

Die Zugriffsregeln (Paragraf [class.access]) für eine virtuelle Funktion werden durch ihre Erklärung bestimmt und nicht von den Regeln für eine Funktion betroffen, die es später außer Kraft setzt. [Beispiel:

class B { 
public: 
    virtual int f(); 
}; 

class D : public B { 
private: 
    int f(); 
}; 

void f() { 
    D d; 
    B* pb = &d; 
    D* pd = &d; 

    pb->f();      // OK: B​::​f() is public, D​::​f() is invoked 
    pd->f();      // error: D​::​f() is private 
} 

- Ende Beispiel]

Dies hält virtuelle Funktionen in Einklang mit den Liskov Substitution Principle

0

löschen Sie die abgeleitete Klasse durch die Basisklasse Zeiger. Mit Hilfe des virtuellen Destruktors starten Sie das Löschen an der abgeleiteten Klasse. Da diese Klasse auf ihre privaten Member zugreifen kann, ruft sie den privaten Destruktor auf. Danach ruft die Basisklasse den öffentlichen Destruktor auf.

Beachten Sie, dass Sie löschen aufrufen und de Destruktor nicht aufrufen abgeleitet :: ~ Derived() direkt!