4. 模板——现代C++风格的基础
Stroustrup于1988年首次公布了与模板(template)有关的语法设计。毫无疑问,这是一项对现代C++的语言风格影响最大的技术改进。模板的概念来自Clu语言,并综合了Smalltalk和Ada语言中相关技术的优点。1991年后,包含模板机制的开发环境(DEC C++、IBM C++、Borland C++等)陆续问世。但直到1995年STL(Standard Template Library)模板库逐渐发展成熟以后,模板技术才在程序员中迅速普及开来。
下面的例子取自SGI STL的示例代码,它基本反映了使用模板技术后C++代码的整体风格:
template <class InputIterator, class T>
InputIterator find(InputIterator first,
InputIterator last, const T& value)
{
while (first != last && *first != value)
++first;
return first;
}
在这样的C++代码中,除了少数几个关键字和操作符以外,我们几乎找不到多少C语言的痕迹了。模板技术兼顾了类型安全和编码灵活性的双重需求,但它同时也为C++语言引入了一种更加精妙但也较难理解(相对于没有模板的代码而言)的代码风格。许多传统的C语言拥护者讨厌这种风格的代码,但更多的新生代程序员对其钟爱有加。1998年,在ANSI/ISO标准化委员会的支持下,STL被作为标准C++库(Standard C++ Library)的一部分收入了C++国际标准之中。今天,以模板、异常等现代C++技术为代表的语言风格也已在事实上成为了C++世界的“官方风格”。
5. ATL——COM时代的另类C++
除了STL模板库之外,还有一个与模板风格相关的例子。下面的代码片断取自Visual C++自动生成的ATL控件工程:
class ATL_NO_VTABLE CMyATLObj :
public IMyATLObj,
public IpersistStreamInitImpl
<CMyATLObj>,
public IOleControlImpl<CMyATLObj>,
public IOleObjectImpl<CMyATLObj>,
public IoleInPlaceActiveObjectImpl
<CMyATLObj>,
public IViewObjectExImpl<CMyATLObj>,
public IoleInPlaceObjectWindowlessImpl
<CMyATLObj>,
public IPersistStorageImpl<CMyATLObj>,
public IspecifyPropertyPagesImpl
<CMyATLObj>,
public IQuickActivateImpl<CMyATLObj>,
public IDataObjectImpl<CMyATLObj>,
public IProvideClassInfo2Impl
<&__uuidof(CMyATLObj), NULL>,
public CComControl<CMyATLObj>
......
注意控件类CMyATLObj的代码,CMyATLObj类居然是从N个接口类和控件类中派生出来的,类的声明语句中随处可见模板的身影——这就是Microsoft为我们设计的别具一格的ATL风格的代码了。之所以要不惜代价地大量使用模板、多重继承等语言特性,这主要为了适应COM、OLE、ActiveX等在架构上本来就相对复杂的技术体系。但这样一来,使用ATL的代码在所有C++代码中,就拥有了一副异乎寻常的长相了:到处都是尖括号,到处都是以“I”打头的标识符,甚至还有多重尖括号的嵌套……如果要求一个刚学会C++语言的程序员立刻读懂一大段ATL代码,我想,用不了几分钟,他就会被代码中那些晦涩、离奇的语言风格折磨得精神崩溃了。
6. 标准C++——一种全新的语言?
C++语言的标准化进程远远落后于语言本身的普及速度。1990年以后,ANSI/ISO的C++标准化委员会才将包括Stroustrup在内的大批专家以及包括Apple、Borland、DEC、HP、IBM、Microsoft、Sun、Unisys在内的知名公司召集在一起,像所有国家的议会或人民代表大会一样通过没完没了的会议、讨论和投票制定C++的国际标准。标准直到1998年9月才正式发布。在国际标准化组织的档案库里,C++标准的代号是ISO/IEC 14882:1998。
Stroustrup建议我们把标准C++当作一种全新的语言来学习[3]。这一说法显然是基于这样一个事实:标准C++语言已经拥有了一种稳定的、可以推广的语言风格,即,通过对STL等既有技术的肯定,ANSI/ISO委员会在1998年的标准中正式认可了包括模板、容器类、I/O流库、异常处理等典型语言特征的现代C++风格。风格的稳定意味着语言本身的进步和成熟,也意味着程序员们对C++的认识必须上升到一个新的层次——那些至今还在编写仅由类和C语言库函数组成的C++代码的程序员,一定会成为Stroustrup及其同仁们的取笑对象的。
Stroustrup的《C++程序设计语言》第3版对标准C++风格做了最权威的阐释。在Stroustrup等专家学者的号召下,越来越多的项目开始编写符合标准C++风格的代码。这一点在许多开放源代码的项目中体现得特别明显。这多半是由于,使用C++语言的开源项目大多都不会像大企业里的项目组那样,在语言风格上会受到公司背景或历史习惯的羁绊。在具体的编程实践中,开源项目的程序员们一方面可以坚决地贯彻标准C++的语言风格,另一方面也可以根据自己的喜好为代码增添一些感情色彩。例如,在OpenOffice的源码中,标识符的前缀规范就相当有特点,连指针和引用类型的变量都由不同的前缀字母区分;下面给出的Linux桌面管理器KDE 3.1.4的源代码片断则显示出,开发KDE的程序员在代码风格上或多或少受到了Java语言风格的影响:
class delUser: public KDialogBase {
Q_OBJECT
public:
delUser(KUser *AUser, QWidget *parent = 0,
const char *name = 0);
bool getDeleteHomeDir()
{ return m_deleteHomeDir->isChecked(); }
bool getDeleteMailBox()
{ return m_deleteMailBox->isChecked(); }
private:
QCheckBox *m_deleteHomeDir;
QCheckBox *m_deleteMailBox;
};
7. 读不懂的代码——兼容并包的语言风格
说到标准C++语言风格,有必要给大家看一段非常古怪但也非常有趣的代码。你看得懂下面这段C++代码吗?它是真正的C++代码吗?
%:include <iostream>
using namespace std;
%:define MAX 5
void main()
<%
int m<:MAX:>;
int i = 1;
for (i = 0; i < MAX; i++)
<%
m<:i:> = i;
if (i not_eq 3 and i < 5)
cout << i << endl;
%>
%>
这是我自己编写的一段代码。你也许无法在Visual C++环境下运行它,但它的语法的确符合1998年C++标准的规定。在GNU C++环境下,我曾成功地将其编译为可执行程序。
简单说来,这段风格诡异的C++代码其实是根据C++标准中关于可替换标记(Alternative Tokens)的规定而编写的。该规定的设计初衷是要适应欧洲某些国家的标准字符集缺少“{”、“#”等标点符号(特别是在一些传统的终端设备上)的现状。严格地讲,这算不得一种真正的语言风格,但类似的规定的确体现了ANSI/ISO委员会在语言设计上兼容并包的宽广胸襟。