起初,C++仅仅是增补了面向对象特性的C语言。甚至C++原始的名称都叫做“C with Classes”,即“使用类的C语言”,二者的继承关系可见一斑。
随着C++逐渐成熟,它开始采纳一些与“使用类的C语言”不同的理念、特征以及编程策略,使其显得更加大胆前卫,更富有冒险精神。在组织各个函数时异常处理机制需要不同的处理方式(参见条目29),模板为设计模式带来了全新的思维方式(参见条目41),与此同时,STL使C++拥有了前所未有的可扩展性。
今天的C++是一门多范型编程语言,它包含面向过程、面向对象、函数式编程、泛型、元编程等等特征。C++的强大和灵活几乎是无可比拟的,但这也会造成一些困惑。所有“恰当用法”的准则似乎都存在例外。那么我们该如何正确理解这样一门语言呢?
最简单的办法就是把C++看作一个由若干门语言组成的联合体,而不是一门单一的语言。在某个特定的子语言中,规则就会趋于简单、直接,而且容易记忆。当你切换到另一门子语言时,规则也就相应地改变了。为了理解C++,你必须能够辨别C++所有主要的子语言。幸运的是,主要的子语言只有四门:
l C。尽管变革是深刻的,C++仍然基于C语言。程序块、语句、预处理器、内建数据类型、数组、指针,等等,所有都来自于C。在许多情况下,C++为某些问题提供的解决方案要比C更优秀(比如条目2(预处理器的替代方法)和条目13(使用对象管理资源)),但是当你发现你正在使用“C++中的C”这一部分编写程序时,高效编程原则就会反映出C语言更多的局限所在:没有模板、没有异常处理、没有重载,等等。
l 面向对象的C++。这一部分的C++就是“使用类的C语言”的一切:类(包括构造函数和析构函数)、封装、继承、多态、虚函数(动态绑定),等等。这是C++中面向对象的经典准则得到最为直接的应用的那一部分。
l 包含模板的C++。这是C++中泛型编程的一部分,这也是大多数程序员涉足最浅的部分。模板的概念遍及C++的方方面面,因此,某些优秀的编程守则中有一些特定的、仅针对模板的段落也不足为奇。(比如,条目46中介绍的在调用模板函数时如何简化类型转换)。事实上,模板如此之强大,它足以带来一个全新的编程范型:模板元编程(template meta-programming,简称TMP)。条目48是对TMP的一个简介,然而除非你是一个狂热的“模板迷”,你大可不必投入过多精力。主流C++编程很少涉及到TMP规则。
l STL。顾名思义,STL是一个模板库,但是它是一个非常特别的模板库。它将容器、迭代器、算法、函数对象之间的约定十分优雅的相互协调在一起,当然模板和库也可以基于其它的理念来构建。STL有自己独特的解决问题的方法,当你使用STL编程时,你必须要遵循它的约定。
时刻地对这四门子语言保持头脑清醒,当你从一门子语言切换到另一门时,高效的程序会要求你必须更改当前策略,遇到这种情况时请不要大惊小怪。比如说,对内建(比如类似C语言的)类型而言,传值要比传引用更高效,但是当你从“C++中的C”迁移到“面向对象的C++”后,构造函数和析构函数的存在就意味着传递const引用会更好。在使用“包含模板的C++”时这一点尤其正确,因为你根本就不知道当前正在处理的对象是什么类型。然而当开始使用STL时,迭代器和函数对象都是基于C语言中的指针机制创建的,因此对于迭代器和函数对象而言,C语言的传值规则又再次奏效了。(关于各种传参方法方案选择的细节,参见条目20。)
综上,C++并不是一门仅仅拥有一套规则的单一化编程语言,它是四门子语言的联合体,每门子语言都有自己的约定。对这四门子语言时刻保持清醒,你会发现C++并没有那么难于理解。
时刻牢记
l C++ 的高效编程守则不是一成不变的,它根据你正在使用的那一部分C++而改变。