c++ - 当我可以确定我可以从 c++ 类继承?
问题描述
假设我使用了一个包含各种类的外部库。我什么时候可以安全地从这些类之一继承?我知道基类必须有一个虚拟析构函数。在使用该类作为基类之前我还应该检查什么?我可以确定只有文档说明它是安全的吗?
解决方案
如果文档声明派生类型是安全的,请遵循文档。如果由于某种原因,它的行为方式与文档背道而驰,那么这是库的问题,并且是作者修复或提供解决方法的错误,因为他们没有承诺他们保证的 API在文档中。
任何不是的类型final
都可以“安全地”派生;更重要的是如何处理和销毁这种类型。如果您从没有析构函数的类型继承virtual
,这本质上不会破坏任何东西;如果您将其从句柄破坏到基类,它只会阻止调用派生类型的析构函数。
如果您只将类型从句柄销毁到派生类型(例如,您要么具体持有它,要么从不从句柄到基类销毁它),那么这没有任何后果。
为了更好地解释我的观点,想象以下层次结构:
class Base {
public:
// No virtual destructor
...
};
class Derived : public Base {
public:
...
private:
std::string m_something; // some leakable object
};
Derived
from的推导Base
是完全安全的。重要的是如何销毁它是否会出现问题。为此,需要考虑两种不同的情况:自动和动态情况。
自动对象
自动类型(“按值”类型)是安全的,无论它们是否具有静态生命周期
auto d = Derived{ ... };
static auto sd = Derived{ ... };
在它们的生命周期结束时,Derived::~Derived
将调用析构函数,因为类型是具体已知的
动态对象
动态对象不会自行销毁。他们的资源最终需要被清理,要么通过智能指针中的 RAII 自动清理,要么由某人调用delete
,或者由某人显式调用~T()
并释放内存。
如果它们被派生类型的句柄销毁,它们仍然是安全的,但如果它们被基类的句柄销毁,它们就不是安全的了。
auto* d1 = new Derived{ ... };
auto* d2 = new Derived{ ... };
// Deleting as a pointer to Base; ~Derived won't be called because ~Base is virtual
// This would be a memory leak
delete static_cast<Base*>(d1); // bad
// Deleting as a pointer to Derived -- ~Derived will be called, this is fine
delete d2; // good
在智能指针类型方面:
共享指针shared_ptr
类型是安全的,因为它们总是从具体类型中销毁对象——即使它们被别名为基类。
void accept_base(std::shared_ptr<Base> b);
auto d = std::make_shared<Derived>(...);
// still safe
accept_base(std::move(d));
唯一指针
unique_ptr
默认情况下,类型是不安全的,T
因为默认删除器会根据unique_ptr
.
例如:
auto d = std::make_unique<Derived>(...);
auto b = std::unique_ptr<Base>{std::move(d)};
// b will be destroyed at end of scope by calling ~Base, which is not virtual!
即使说了这么多:如果您使用的库明确声明您打算派生一些 XYZ 类,那么您仍然应该假设这就是该类的使用方式。那时,如果发生不希望的事情,则由库维护者来确保他们的代码按文档说明执行,因为它是他们明确声明的 API 的一部分。
推荐阅读
- python - 如何在没有 if/else 语句的情况下在 for 循环中添加日期
- javascript - 分配给变量的函数返回未定义
- solr - 如何使用 IPAccessHandler 限制集群 Solr 环境中的 IP?
- python - 在张量上使用 while_loop 在张量流中创建掩码
- vb.net - 尝试使用参数运行 .exe,出现有关提升的错误
- python - 试图制作一个在python3中提取电子邮件的函数
- agent - 如何使用状态图作为代理的流程图
- c - C 中的 Cast-expression 语义
- python - 是否可以以一定的速度发送数据(每秒 x 项)
- sqlite - 如何绕过 Heroku slug 大小限制?