为什么STL的容器不加final?

大部分STL容器的析构函数都是非虚的,如果被自定义容器继承了的话在多态下会内存泄漏吧,那为什么不加final呢?

1

析构函数非虚的基类并不是说不应该继承,而是说不应该在继承了以后拿父类指针去释放,否则对象就会被撕裂。

何况,并不是所有继承都是为了多态。如果干脆拿不到父类指针也行,比如:

class Buffer : private std::vector<uint8_t> {
	// ... 
}; 

LWG issue 2113 就有人提出「实现 STL 时是不是可以自行给非多态容器加 final?」。详细的讨论内容可以点链接进去看,总之讨论结果是 NAD(非缺陷),只要是标准中未注明 final 的,用户就有权去继承,所以标准库实现不能自行添加 final。

于是,从 C++17 标准开始就多了一句:All types specified in the C++ standard library shall be non-finaltypes unless otherwise specified.

http://eel.is/c++draft/derivation#4

2

没有虚函数又何来多态呢,你继承了之后cast回父类又能做什么。

3

C++有多种编程风格,面向对象只不过是其中一种而已。有的人对面向对象极端厌恶,甚至水火不容,STL的作者就是。

STL之父Alexander Stepanov说过:I think that object orientedness is almost as much of a hoax as Artificial Intelligence.

所以STL从头到尾都不是用面向对象的思想构建的。

他接受参访的时候自己也说过,STL不是面向对象的:

Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper's Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras - families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting - saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms - you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.

http://www.stlport.org/resources/StepanovUSA.html

4

当你的class需要一个没有虚函数的base去继承的时候,你应该用的其实是delegation,而不是继承。因为这时你需要的不是 is a的关系,而是has a的关系。你的代码结构会更简单,且更容易被测试。

5

经典的 private 继承用法之一就是防止外界拿到父指针然后瞎调用非虚函数。所以要解决题目描述里的问题只需要 private 继承即可

6

没虚函数,你继承了用不了多态,正常继承定制容器也可行。。另外有些组件是有继承使用的案例的,比如std::proiority_queue

7

C++不像java,如果没有声明虚函数,对象里就不会有虚表指针,而java不管你是啥类都默认有。stl容器没有虚函数,所以不存在误用,继承了之后同名函数只会覆盖而不是重写,没法进行子类型多态,你的前提就不成立。

以及C++中继承并不是全为了多态,这两个没有必然联系,继承也可以是用于实现组合,例如使用private继承来组合,stl不能随便禁止用户的这种做法。

8

TL;DR final 是 OO 的产物,但是 C++ 是多范式的,final 的使用会影响其他范式的应用。

OOP 里面 final 的确足以表达作者的想法:这个类不应该被继承。

但是 C++ 是一个多范式的,如果类的使用者是 generic 或者是 object-based,final 就会严重妨碍使用者(generic 里面大量的 wapper 都是基于继承的)。在这两种环境下,“继承” 没有 is-a/has-a/is-implement-of 的语义。继承一个类往往是为了复用或者是模拟。所以这时,“继承一个不该继承的类” 并不会有很大的影响(“因为 ‘这个类不该被继承’ 更多是从 OO 的角度考虑”)。

标准库很少用 final 是因为它本身是 object-based 而不是 object-oriented。

Java 中 final 很常用很强大是因为 Java 是 (几乎纯)OO 的。

override 好用是因为只要你重写,几乎总是 OOP。

更好的做法是:违反 final 引发一个警告而不是错误(标准里要求一旦违反,必须是一个 error)。

问题链接

https://www.zhihu.com/question