人们普遍认为,C++标准库通常不打算使用继承进行扩展.当然,我(以及其他人)批评那些建议来自诸如此类的人std::vector
.但是,这个问题:c ++异常,可以what()为NULL吗?让我意识到标准库至少有一部分是为了扩展 - std::exception
.
所以,我的问题有两个部分:
是否有其他标准库类可以派生自哪些?
如果一个派生自标准库类,例如std::exception
,是否受ISO标准中描述的接口约束?例如,使用异常类的what()
成员函数的程序是否会返回NTBS(比如它返回一个空指针)是否符合标准?
D.S*_*ley 38
好问题.我真的希望标准对于预期用途更加明确.也许应该有一个与语言标准并列的C++ Rationale文档.无论如何,这是我使用的方法:
(a)我不知道是否存在任何此类清单.相反,我使用以下列表来确定标准库类型是否可能被设计为继承自:
virtual
方法,那么你不应该使用它作为基础.这排除了std::vector
等等.virtual
方法,那么它是用作基类的候选者.friend
语句浮动,那么转向清楚,因为可能存在封装问题.std::char_traits
)的存在是一个非常好的线索,你不应该使用它作为基础.不幸的是,我不知道一个很好的综合或黑白名单.我通常会感觉到直觉.
(b)我会在这里申请LSP.如果有人调用what()
你的异常,那么它的可观察行为应该与之相匹配std::exception
.我认为这不是一个标准一致性问题,而是一个正确性问题.标准不要求子类可替代基类.这真的只是一个"最佳实践".
小智 7
关于你的b部分,从17.3.1.2"要求",第1段:
该库可以通过C++程序进行扩展.每个条款(如果适用)描述了此类扩展必须满足的要求.此类扩展通常是以下之一:
- 模板参数
- 派生类
- 满足接口约定的容器,迭代器和/或算法
虽然17.3是信息性的而不是约束力,但委员会对派生阶级行为的意图是明确的.
对于其他非常类似的扩展点,有明确的要求:
在最后一点,我不清楚括号列表是详尽无遗的,但考虑到下一段中如何具体说明每个提到的案例,可以说当前文本旨在涵盖派生类.此外,该17.4.3.6/1的文字是在2008年的选秀不变(如果它在17.6.4.8),我看不出有什么问题解决IT或派生类的虚拟方法.
简约的规则是“任何类都可以用作基类;在没有虚拟方法(包括虚拟析构函数)的情况下安全使用它的责任完全由派生作者承担。” 在 std::exception 的子类中添加非 POD 成员与在 std::vector 的派生类中添加非 POD 成员是相同的用户错误。容器并非“旨在”成为基类的想法是文学教授所说的“作者意图谬误”的一个工程示例。
IS-A 原则占主导地位。不要从 B 派生 D,除非 D 可以在 B 的公共接口中的各个方面替代 B,包括 B 指针上的删除操作。如果B有虚方法,这个限制就不那么繁重;但如果 B 只有非虚方法,那么专门化继承仍然是可能且合法的。
C++ 是多范式的。模板库使用继承,甚至从没有虚拟析构函数的类继承,因此通过示例证明此类构造是安全且有用的;是否有意为之是一个心理问题。
C++标准库不是一个单元.它是组合和采用几个不同库的结果(C标准库的一大块,iostreams库和STL是三个主要构建块,并且每个都是独立指定的)
如您所知,库的STL部分通常不是从中派生出来的.它使用通用编程,通常避免使用OOP.
IOStreams库是更传统的OOP,并且在内部使用继承和动态多态 - 并且期望用户使用相同的机制来扩展它.自定义流通常通过派生自流类本身或streambuf
内部使用的类来编写.这两个都具有可以在派生类中重写的虚方法.
std::exception
是另一个例子.
和D.Shawley说的一样,我会将LSP应用到你的第二个问题.将基类替换为派生类应始终是合法的.如果我打电话exception::what()
,它必须遵循exception
该类指定的合同,无论该exception
对象来自何处,或者它是否实际上是一个已被上升的派生类.在这种情况下,该合同是返回NTBS的标准承诺.如果您使派生类的行为不同,那么您违反了标准,因为类型的对象std::exception
不再返回NTBS.