防止指针重新分配

Ima*_*889 8 c++ inheritance pointers

我正在阅读Scott Meyers撰写的Effective C ++第三版。

他说,一般来讲,从不包含虚函数的类中继承不是一个好主意,因为如果您以某种方式将派生类的指针转换为基类的指针,然后再将delete其转换,则可能会出现不确定的行为。

这是他给出的(人为)示例:

class SpecialString: public std::string{
   // ...
}

SpecialString *pss = new SpecialString("Impending Doom");
std::string *ps;
ps = pss;
delete ps;    // undefined! SpecialString destructor won't be called
Run Code Online (Sandbox Code Playgroud)

我理解为什么这会导致错误,但是在SpecialString类内部没有什么可以阻止类似ps = pss事件的发生?

Meyers指出(在本书的另一部分中),显式防止类中允许某些行为的常见技术是声明特定的函数,但有意不对其进行定义。他给出的例子是复制构造。例如,对于您不想允许复制构造的类,声明一个私有复制构造函数但不定义它,因此,任何使用它的尝试都会导致编译时错误。

我意识到ps = pss在此示例中不是复制构造,只是想知道是否可以在此处进行任何操作以明确防止这种情况发生(除了“只是不这样做”的答案外)。

JaM*_*MiT 2

该语言允许从指向派生类的指针到指向其基类的指针的隐式指针转换,只要基类是可访问的并且没有二义性。这不是用户代码可以覆盖的东西。此外,如果基类允许销毁,那么一旦将派生指针转换为基类指针,就可以通过指针删除基类,从而导致未定义的行为。这不能被派生类覆盖。

因此,您不应从未设计为基类的类派生。您的书中缺乏解决方法就表明缺乏解决方法。


上面有两点可能值得再看一下。第一:“只要基类是可访问的并且没有歧义”。(我不想进入“模棱两可”的点。)您可以通过将基类设置为private. 但是,如果您这样做,您应该花一些时间思考一下为什么要继承。私有继承通常很少见。通常,不从其他类派生而是使用类型为其他类的数据成员会更有意义(或至少同样有意义)。

第二:“如果基类允许破坏”。这不适用于您无法更改基类定义的示例,但它确实适用于声明“通常从不包含虚拟[析构函数]的类继承不是一个好主意”。还有另一个可行的选择。如果该类的析构函数是 ,那么从没有虚函数的类继承可能是合理的protected。如果类的析构函数受到保护,则不允许使用delete指向该类的指针(在该类及其派生类的实现之外)。因此,只要基类具有虚拟析构函数或受保护的析构函数,就可以避免未定义的行为。