我理解,对于公共继承,它通常不是安全的,因为当delete
基类指针时,编译器只生成调用基类的析构函数的代码,以及派生类的一个人没有被召唤。
但是对于私有继承,客户端无法将派生类指针强制转换为基类指针(因为私有继承不会模拟 is-a 关系),因此始终使用delete
在派生类的指针上,编译器应该能够看到还有一个基类并调用它的析构函数。
我做了这个测试:
#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';
}
GCC 4.7.1和CLang 3.1都提供相同的输出。调用派生类构造函数,除非派生类本身将派生类指针强制转换为基类并且delete
为它。
除了这个看起来非常罕见且容易避免的案例(班级的作者是唯一可以造成伤害的人,但它确实知道从哪个派生出来的那个人),我能否得出结论它是安全的?
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
奖金问题:保护继承怎么样?我认为做损害的能力不再是直接派生类作者的特权,而是层次结构中任何类的作者。
答案 0 :(得分:10)
继承是公共还是私有不会影响代码的安全性,它只会限制安全/不安全使用的范围。您有相同的基本问题:如果您的类或您的类的朋友将您的类型的对象传递给一个接口,该接口在没有虚拟析构函数的情况下获取指向基础的指针,并且该接口获得对象的所有权,那么您创建的是未定义的行为。
设计中的问题是,根据您的问题,BaseNonVirtual
并非旨在扩展。如果是,它应该具有公共虚拟析构函数或受保护的非虚拟析构函数,确保没有代码能够通过指向基础的指针在派生对象上调用delete。
答案 1 :(得分:1)
有一种情况是客户端代码可以将Derived转换为Base,尽管有私有继承:
delete reinterpret_cast<BaseNonVirtual*>(new DerivedPrivNonVirtual);
因此跳过~DerivedPrivNonVirtual()
的执行。
但是,考虑到不鼓励使用reinterpret_cast
多少,你可以得出结论:它足够安全&#34;为了你的目的。