C++ 当我可以确定我可以从c++;上课?

C++ 当我可以确定我可以从c++;上课?,c++,C++,假设我使用了一个包含各种类的外部库。什么时候我可以安全地从其中一个类继承?我知道基类必须有一个虚拟析构函数。在使用该类作为基类之前,是否还需要检查其他内容?我是否可以确保只有在文档声明是安全的情况下它才是安全的?如果文档声明派生类型是安全的,请按照文档进行操作。如果由于某种原因,它的行为与文档不符,那么这就是库的问题,也是作者需要修复或提供解决方法的错误,因为他们没有承诺文档中保证的API 任何不是final的类型都可以“安全地”派生;更重要的是如何处理和销毁这种类型。如果您继承的类型没有vi

假设我使用了一个包含各种类的外部库。什么时候我可以安全地从其中一个类继承?我知道基类必须有一个虚拟析构函数。在使用该类作为基类之前,是否还需要检查其他内容?我是否可以确保只有在文档声明是安全的情况下它才是安全的?

如果文档声明派生类型是安全的,请按照文档进行操作。如果由于某种原因,它的行为与文档不符,那么这就是库的问题,也是作者需要修复或提供解决方法的错误,因为他们没有承诺文档中保证的API


任何不是
final
的类型都可以“安全地”派生;更重要的是如何处理和销毁这种类型。如果您继承的类型没有virtual析构函数,那么这不会从本质上破坏任何东西;它只是防止在从句柄到基销毁派生类型的析构函数时调用该析构函数

如果您只将类型从句柄销毁到派生类型(例如,您要么具体地保存它,要么从不将它从句柄销毁到基),那么这没有任何后果

为了更好地解释我的观点,请设想以下层次结构:

类基{
公众:
//没有虚拟析构函数
...
};
派生类:公共基{
公众:
...
私人:
std::string m_something;//某个可泄漏对象
};
Base
派生
是完全安全的。重要的是它如何被破坏,是否会有问题。为此,需要考虑两种不同的情况:自动和动态情况

自动对象 自动类型(“按值”类型)是安全的,无论它们是否具有静态生存期

autod=Derived{…};
静态自动sd=派生的{…};
在其生命周期结束时,将调用析构函数
Derived::~Derived
,因为类型是具体已知的

动态对象 动态对象不会自行销毁。他们的资源最终需要清理,要么使用智能指针中的RAII自动清理,要么有人调用
delete
,要么有人显式调用
~T()
并释放内存

如果它们被派生类型的句柄破坏,它们仍然是安全的,但如果它们被基类型的句柄破坏,它们就不会安全

auto*d1=新派生的{…};
auto*d2=新派生的{…};
//作为指向基的指针删除~无法调用派生,因为~Base是虚拟的
//这将是内存泄漏
删除静态_cast(d1);//坏的
//作为指向派生--~Derived的指针删除将被调用,这很好
删除d2;//好的
就智能指针类型而言:

共享指针
shared_ptr
类型是安全的,因为它们总是从具体类型中销毁对象——即使它们被别名为基类

void accept_base(std::shared_ptr b);
自动d=std::使_共享(…);
//仍然安全
接受基础(标准::移动(d));
唯一指针 默认情况下,
unique\u ptr
类型不安全,因为默认删除程序基于
unique\u ptr
T
类型进行删除

例如:

autod=std::使_唯一(…);
自动b=std::unique_ptr{std::move(d)};
//b将在作用域结束时通过调用~Base销毁,这不是虚拟的!


即使如此:如果您使用的库明确声明您要派生某个XYZ类,那么您仍然应该假设这就是该类的使用方式。在这一点上,如果出现了不希望出现的情况,则应由库维护人员确保其代码按文档所述执行,因为它是其明确声明的API的一部分。

如果您打算从基类引用或指针调用方法,还应检查它们是否声明为虚拟的

除此之外,我还将研究该类的文档,以及它是否声明为
final

实际上,不需要虚拟析构函数,就可以安全地从类继承。如果希望使用(从而销毁)指向基类的指针中的类,则只需要一个虚拟析构函数

这完全取决于您打算如何使用派生类

例如,如果您只想创建一个从给定类继承的类,但不想在基类指针或引用中使用它

Base baseObj;
Derived derivedObject; // This does not create any problems
如果要从指向基类的指针或引用(当然,这也适用于智能指针)使用它,如下所示:

Base* basePtr = new Base();
Base* basePtrToDerived = new Derived();
Derived* derivedPtrToDerived = new Derived();
// Do stuff here
delete basePtr;
delete basePtrToDerived; // if Base has no virtual destructor, only the destructor of Base is called
delete derivedPtrToDerived; // This will always call the destructor of Derived

你需要一个虚拟析构函数。

你说的“安全”是什么意思?“我知道基类必须有一个虚拟析构函数。”这不是真的。可以从类继承而不使用虚拟析构函数。当您通过指向基类的指针销毁对象时,您需要一个虚拟析构函数来实现多态性。没有虚拟析构函数并不意味着它“不安全”,这取决于对派生类型的处理。如果它被没有虚拟析构函数的基指针破坏,那么它可能是坏的,因为派生指针不会调用它的析构函数。如果它作为派生类型被销毁,这很好。@Pipetus不会发生任何有趣的事情,不会发生数据损坏,也不会出现某种意外的阴影。没有奇怪的特定于实现的行为。我不关心ABI的兼容性或使用的方便性。这回答了问题吗?您可以安全地从没有
final
说明符的任何类继承。同样重要的是:如何使用派生类?如果您误用了基类和派生类之间的关系,那么可能会发生不好的事情。这是否意味着虚拟构造函数真的是唯一的