【发布时间】:2010-12-11 11:07:08
【问题描述】:
有人知道为什么 STL 容器没有虚拟析构函数吗?
据我所知,唯一的好处是:
- 它将实例的大小减少了一个指针(指向虚拟方法表)和
- 它使破坏和建造稍微快一点。
缺点是以通常的方式对容器进行子类化是不安全的。
编辑: 也许我的问题可以改写为“为什么不设计 STL 容器以允许继承?”
因为它们不支持继承,所以当人们想要拥有一个需要 STL 功能和少量附加功能的新容器(例如,专门的构造函数或具有默认值的新访问器地图,或其他):
- 组合和接口复制:创建一个新的模板或类,它拥有作为私有成员的 STL 容器,并且每个 STL 方法都有一个直通内联方法。这与继承一样高效,避免了虚拟方法表的成本(在重要的情况下)。不幸的是,STL 容器具有相当广泛的接口,因此这需要很多行代码才能完成看似容易做到的事情。
- 只需制作函数:使用裸(可能是模板化的)文件范围函数,而不是尝试添加成员函数。在某些方面,这可能是一种不错的方法,但会失去封装的好处。
- 具有公共 STL 访问权限的组合:让 STL 容器的所有者允许用户访问 STL 容器本身(可能通过访问器进行保护)。这需要对库编写器进行最少的编码,但对用户来说不太方便。组合的一大卖点是减少了代码中的耦合,但此解决方案将 STL 容器与所有者容器完全耦合(因为所有者返回一个真正的 STL 容器)。
- 编译时多态性:编写起来可能有些棘手,需要一些代码练习,并不适合所有情况。
作为一个附带问题:是否有一种标准安全的方式来使用非虚拟析构函数进行子类化(假设我不想覆盖任何方法,只是想添加新方法)?我的印象是,如果没有能力更改定义非虚拟类的代码,就没有通用且安全的方法。
编辑 2:
作为@doc points out,C++ 11 更高级的using 声明在一定程度上降低了组合成本。
【问题讨论】:
-
您错过了“阻止程序员不恰当地使用继承”的好处。从 STL 容器继承可能有正当理由,但我从未找到。
-
回答你的问题:
Prefer Composition to Inheritance. -
“在某些方面 [非成员函数] 可能是一个好方法,但封装的好处丢失了”。这是错误的。当您仅使用公共接口工作时,完全尊重封装。您可以操作容器(如搜索/排序算法),也可以调整它(如 std::stack)。不要将成员函数语法误认为是抽象的封装,它们是完全独立的东西。
-
@MatthieuM。如果您的类最好被描述为前一个类的子类型,那么您应该继承。如果您正在创建一个名为
MyVector的类,它向标准std::vector<int>添加了一个方法,那么不要告诉我组合适合这种情况。通过正确使用继承,您可能会收获很多。如果 STL 类没有虚拟 dtors/方法来保持 vtable 远离,那么您可以通过使用继承来获得相同的结果。通过组合,您的类将随着组合成员的大小而增长 - 类似于创建 vtable 的成本。 -
@MatthieuM。恐怕不明白的不是我。您应该参考我的论点,而不是盲目地重复“口头禅”(正如您正确称呼的那样)。并且不要将继承与多态性混淆。如果类没有虚方法,不代表不能继承。
标签: c++ stl destructor