C++之父谈C++语言设计规则( 六 )


允许一个有用的特征比防止各种错误使用更重要:你可以在任何语言里写出很坏的程序 。 真正重要的问题 , 是尽可能减少偶然用错某些特征的机会 。 我们花了很多精力 , 去保证C++ 里各种构造的默认行为或者是有意义的 , 或者将导致编译错误 。 例如 , 按默认方式 , 所有函数的参数类型都要做检查 , 即使是跨过了分别编译的边界;还有 , 按默认方式 , 所有的类成员都是私用的 。 当然 , 一个系统程序设计语言不应该禁止程序员有意打破系统的限制 , 所以设计的努力应该更多地放在提供一些机制 , 帮助人写出好程序方面 , 而不是放在禁止不可避免的坏程序方面 。 在长期的工作中 , 程序员必然会学习 。 这种观点也是C语言传统上的“相信程序员”口号的一种变形 。 提供各种类型检查和访问控制规则 , 使类的提供者能清楚地表述其对类的使用者期望些什么 , 并提供保护以防偶然事故 。 这种规则并不想提供一种保护机制 , 禁止有意违反规则的情况(2.10节) 。
支持从分别开发的部分出发进行软件的组合:复杂应用需要比简单程序更多的支持 , 大程序需要比小程序更多的支持 , 效率约束很强的程序需要比资源丰富的程序更多的支持 。 在第三个条件的约束下 , C++ 的设计中花了很多精力去解决前两个问题 。 当实际应用变得更大、更复杂时 , 这些应用必然是由一些人们能把握的具有一定独立性的部分组合而成的 。
任何能用于独立进行大系统的部件开发 , 而后又允许将它们不加修改地用到大系统里的东西 , 都可以服务于这一目标 。 C++ 的许多发展都是由这一思想推动的 。 从根本上说 , 类本身就是这样的C++特征 , 抽象类(13.2.2节)能显式支持接口与实现分离 。 事实上 , 类可以用于表述一系列互相联系的策略 [Stroustrup , 1990b] 。 异常机制允许从一个库出发去处理错误(16.1节) , 模板使人能基于类型进行组合(15.3节、15.6节和15.8节) , 名字空间解决名字污染问题(17.2节) , 而运行时类型识别能处理这样一类问题:当一个对象在传递过程中穿过一个库时 , 其准确类型有可能丢失 , 在这种情况下应该怎么办?
程序员在开发大系统时需要得到更多的帮助 , 还意味着不能过分依赖只对小程序有特效的优化技术 , 从而造成效率的损失 。 因此 , 对象布局应该能在特定编译单位内部孤立地确定 , 而虚函数调用也应该能编译成有效代码 , 不依靠跨越编译单位的优化 。 这些确实都做到了 , 甚至在高效意味着与C相比非常有效的意义下 。 如果有关整个程序的信息都能用 , 再做一些优化也是可能的 。 例如 , 通过检查整个程序 , 一个对虚函数的调用——在不牵涉动态链的情况下——有时可以确定为一个实际的函数调用 。 在这种情况下 , 就可以用一个正常的函数调用取代虚函数调用 , 甚至用inline的方式取代 。 现在已经存在能做这种事的C++ 实现 。 当然 , 对于生成高效代码 , 这种优化并不是必需的 , 它们不过是在希望更高的运行效率 , 而不是编译效率和动态连接新派生类的情况下 , 可以获得的一些附加利益 。 当无法合理地做到这类全局优化时 , 还是可以通过优化去掉一些虚函数调用 , 只要该虚函数是应用在已知类型的对象上 , Cfront的Release 1.0就能做这件事 。
对大系统的支持 , 经常是在“对于库的支持”的题目下讨论的(第8章) 。
4.4 语言的技术性规则下面规则针对的问题是如何在C++里表述各种事物 , 这里不讨论能表述什么 。
C++之父谈C++语言设计规则文章插图
不隐式地违反静态类型系统:每个对象在建立时就具有特定的类型 , 例如double、char*或dial_buffer等 。 如果以与对象的类型不一致的方式去使用它 , 那就是违背了类型系统 。 绝不允许这种情况发生的语言称为是强类型的 。 如果一种语言能在编译时确认所有违反类型系统的情况 , 那么它就是强静态类型的 。