有人知道为什么STL容器没有虚析构函数吗?
据我所知,唯一的好处是:
- 它将示例的大小减少一个指针(指向虚方法表)
- 它使破坏和建设的速度稍微快了一点。
缺点是用通常的方法子类化容器是不安全的。
我的问题的另一种表达方式是“为什么STL容器没有被设计成允许继承?”
因为它们不支持继承,所以当你想拥有一个需要STL功能加上少量附加特性(比如一个专门的构造函数或带有map默认值的新访问器,或者其他什么)的新容器时,你只能选择以下几种方法:
- 组合和接口复制 *:创建一个新的模板或类,将STL容器作为私有成员,并且每个STL方法都有一个传递内联方法。这和继承一样高效,避免了虚方法表的成本(在重要的情况下)。不幸的是,STL容器有相当宽的接口,所以这需要很多行代码来完成一些看起来应该很容易做的事情。
- 只需创建函数 *:使用空的(可能是模板化的)文件作用域函数,而不是尝试添加成员函数。在某些方面,这可能是一个很好的方法,但是封装的好处就丧失了。
- 使用公共STL访问进行合成 *:让STL容器的所有者允许用户访问STL容器本身(可能通过访问器保护)。这对库编写者来说需要最少的编码,但对用户来说却不太方便。组合的一个重要卖点是减少了代码中的耦合,但这种解决方案将STL容器与所有者容器完全耦合(因为所有者返回一个真正的STL容器)。
- 编译时多态性 *:要正确执行可能有些棘手,需要一些代码技巧,并且不适合所有情况。
作为附带问题:是否有一种标准安全的方法来使用非虚析构函数进行子类化(假设我不想覆盖任何方法,只想添加新的方法)?我的印象是,如果没有权力更改定义非虚类的代码,那么就没有通用和安全的方法来实现这一点。
9条答案
按热度按时间px9o7tmv1#
虚析构函数只在继承场景中有用。STL容器不是设计来继承的(也不是支持的场景)。因此它们没有虚析构函数。
643ylb082#
我认为Stroustrup间接回答了这个问题在他的梦幻般的文件:Why C++ is not just an ObjectOriented Programming Language:
7闭幕词
上面介绍的各种设施是不是面向对象的?哪些设施?使用面向对象的什么定义?在大多数上下文中,我认为这些都是错误的问题。重要的是你能清楚地表达什么想法,你能多容易地合并来自不同来源的软件,以及所产生的程序有多高效和可维护。换句话说,你如何支持好的编程技术和好的设计技术比标签和流行语更重要。2基本的思想是简单地通过抽象来改进设计和编程。3你想隐藏细节,你想利用系统中的任何共性,而且你想让它负担得起。我想鼓励你不要让面向对象成为一个毫无意义的术语。“面向对象”的概念经常被贬低
我已经论证过,除了面向对象编程和设计之外,还有--而且必须有--有用的技术。然而,为了避免被完全误解,我想强调的是,我不会尝试使用一种至少不支持面向对象编程的经典概念的编程语言来进行一个严肃的项目。除了支持面向对象编程的工具之外,我希望--而且C提供了--超越那些支持直接表达概念和关系的特性。
STL的建立主要考虑了三个概念性的工具。通用编程+函数式风格+数据抽象== STL风格。OOP不是表示数据结构和算法库的最佳方式并不奇怪。尽管OOP用于标准库的其他部分,STL的设计者看到了上述三种技术的混合比OOP * 单独 * 更好。简而言之,这个库在设计时并没有考虑到OOP,在C中,如果你不使用它,它不会捆绑在你的代码中。你不用为你不使用的东西付费。类std::vector,std::list,......不是Java/C#意义上的OOP概念。它们只是最好的解释中的抽象数据类型。
kninwzqo3#
我想这是遵循了C++的哲学,即不为不使用的特性付费。根据平台的不同,如果你不关心虚拟析构函数,那么虚拟表的指针可能是一个沉重的代价。
zynd9foi4#
为什么STL容器的设计不允许继承?
以我的拙见,它们是。如果它们不是,它们就已经是 final 了。当我查看
stl_vector.h
源代码时,我可以看到我的STL实现使用_Vector_base<_Tp, _Alloc>
的 protected 继承来授予派生类的访问权限:如果子类化不受欢迎,它不会使用 private 继承吗?
是否有一种标准安全的方法来使用非虚析构函数进行子类化(假设我不想覆盖任何方法,只想添加新的方法)?
为什么不使用
protected
或private
继承,并使用using
关键字公开接口的所需部分?这种方法确保了类的用户不会将示例向上转换为
std::vector<int>
,并且他是安全的,因为非虚拟析构函数的唯一问题是,当对象作为父类的示例被删除时,它不会调用派生的析构函数。...我也有一个松散的想法,你甚至可以公开继承,如果你的类没有析构函数.异端?
fkaflof65#
你不应该盲目地给每个类添加虚析构函数,如果是这样的话,语言就不允许你有其他选择了,当你给一个没有虚方法的类添加虚方法时,你只是把类示例的大小增加了一个指针的大小,通常是4个字节。根据你所做的事情,这是很昂贵的。大小的增加是因为创建了一个v-table来保存虚方法的列表,每个示例都需要一个指向v-table的指针,它通常位于示例的第一个单元格。
sg24os4d6#
另一个能够从STL容器子类化的解决方案是由Bo Qian使用智能指针给出的。
Advanced C++: Virtual Destructor and Smart Destructor
这完全避免了虚析构函数的麻烦。
kx5bkwkv7#
正如已经指出的,STL容器不是被设计成可继承的。没有虚方法,所有数据成员都是私有的,没有受保护的getter/setter/helper..正如你已经发现的,没有虚析构函数..
我建议您应该真正通过组合而不是实现继承来使用容器,以“has-a”的方式而不是“is-a”的方式。
6jjcrrmo8#
如果你真的需要虚析构函数,你可以把它添加到从vector〈〉派生的类中,然后在你需要虚接口的任何地方使用这个类作为基类。这样做,编译器将从你的基类调用虚析构函数,后者又会从vector类调用非虚析构函数。
示例:
此代码提供以下输出:
csga3l589#
没有虚析构函数阻止类正确地成为子类。