Rozumiem, że przy dziedziczeniu publicznym ogólnie jest to , a nie bezpieczne, ponieważ gdy delete
ma wskaźnik klasy bazowej, kompilator generuje tylko kod do wywołania destruktora klasy podstawowej, a klasa pochodna nie jest wywoływana.Czy można bezpiecznie dziedziczyć z klasy za pomocą nie wirtualnego destruktora?
Ale dla prywatnego dziedziczenia klient nie może rzucić pochodzący klasy wskaźnik do wskaźnika klasy bazowej (jako prywatne dziedziczenie nie modelować is-a związek), więc delete
jest zawsze używany na wskaźniku klasy pochodnej, a kompilator powinien być w stanie zobaczyć, że istnieje również klasa bazowa i wywołać jej destruktor.
że wykonany test:
#include <iostream>
struct BaseVirtual
{
virtual ~BaseVirtual()
{
std::cout << "BaseVirtual's dtor" << '\n';
}
};
struct BaseNonVirtual
{
~BaseNonVirtual()
{
std::cout << "BaseNonVirtual's dtor" << '\n';
}
};
struct DerivedPrivVirtual: private BaseVirtual
{
static void f()
{
BaseVirtual * p = new DerivedPrivVirtual;
delete p;
}
~DerivedPrivVirtual()
{
std::cout << "DerivedPrivVirtual's dtor" << '\n';
}
};
struct DerivedPrivNonVirtual: private BaseNonVirtual
{
static void f()
{
BaseNonVirtual * p = new DerivedPrivNonVirtual;
delete p;
}
~DerivedPrivNonVirtual()
{
std::cout << "DerivedPrivNonVirtual's dtor" << '\n';
}
};
int main()
{
std::cout << "With explicit derived pointer type:" << '\n';
{
DerivedPrivVirtual * derivedPrivVirtual = new DerivedPrivVirtual;
DerivedPrivNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual;
delete derivedPrivVirtual;
delete derivedPrivNonVirtual;
}
std::cout << '\n';
std::cout << "With base pointer type:" << '\n';
{
// Client code can't cast Derived to Base when inherit privately.
//BaseVirtual * derivedPrivVirtual = new DerivedPrivVirtual;
//BaseNonVirtual * derivedPrivNonVirtual = new DerivedPrivNonVirtual;
//delete derivedPrivVirtual;
//delete derivedPrivNonVirtual;
}
std::cout << '\n';
std::cout << "Inside derived class itself:" << '\n';
{
DerivedPrivVirtual::f();
DerivedPrivNonVirtual::f();
}
std::cout << '\n';
std::cout << "With non-dynamic variables:" << '\n';
{
DerivedPrivVirtual derivedPrivVirtual;
DerivedPrivNonVirtual derivedPrivNonVirtual;
}
std::cout << '\n';
}
Zarówno GCC 4.7.1 i szczęk 3,1 daje taki sam efekt. Wydzielony konstruktor klasy jest wywoływany z wyjątkiem sytuacji, gdy klasa pochodna sama rzuca wskaźnik klasy pochodnej do klasy bazowej i delete
.
Poza tym przypadkiem, który wydaje się dość rzadki i łatwy do uniknięcia (autor klasy jest jedynym facetem, który może wyrządzić szkodę, ale wie, z której klasy pochodzi jego), czy mogę stwierdzić, że jest on bezpieczny?
With explicit derived pointer type:
DerivedPrivVirtual's dtor
BaseVirtual's dtor
DerivedPrivNonVirtual's dtor
BaseNonVirtual's dtor
With base pointer type:
Inside derived class itself:
DerivedPrivVirtual's dtor
BaseVirtual's dtor
BaseNonVirtual's dtor <-- Only a problem inside the class itself
With non-dynamic variables:
DerivedPrivNonVirtual's dtor
BaseNonVirtual's dtor
DerivedPrivVirtual's dtor
BaseVirtual's dtor
Bonus pytanie: co o chronionej dziedziczenia? Przypuszczam, że zdolność czynienia szkód nie jest już właściwa dla bezpośrednio wywodzącego się autora klasy, ale dla autorów dowolnej klasy w hierarchii.
Jeśli dobrze pamiętam, Scott Meyers (autor Effective C++, bardziej efektywny C++) nadal nie wie nawet, co oznacza dziedziczenie chronione. Public jest relacją "is-a", Private jest "zaimplementowany-w-warunkach", ale chroniony? To trochę przerażające. – Borgleader