能有效地表达抽象的思想是人类和其他智能生物的根本区别。同样,可以使用准确的术语进行沟通交流是专业人员的基本的素质。可以这样说,一个人分析问题解决问题的能力正比于其熟练理解的概念的数量。《设计模式》是OO领域的抗鼎之作,其中一个重要的原因是她提供了一个抽象的,定义清晰的词汇集合,尽管后来我们发现其中的一些名词定义还是不够清楚。熟练的C程序员非常清楚诸如常量指针和指针常量的细微差别,而初学者则往往含糊不清。对于C++,这样的术语更是可以列出一个一长串。本文的目的就是澄清这些容易混淆的,在标准文档以及C++社区中广泛使用但是C++使用者却不是很熟悉的术语。初学者可以比对这个列表检查自己的理解,而中级的C++程序员也可以温故而知新。
实参(argument)和形参(parameter)
C++程序员基本可以区别这二者的区别,但是仅仅限于中文的“实参”和“形参”。这里同时标准处英文,就是要让大家记住他们的英文专门名字,这对于理解英文论坛的帖子非常重要。
C++标准中,实参的定义以列表的形式给出:
- 函数调用中被括号包含的逗号分隔的表达式;
- 在类似函数的宏的调用中被括号包含的逗号分隔的一个或多个预处理符号的序列;
- throw 语句的操作符
- 模版实例化的时候,被尖括号包含的逗号分隔的类型,表达式或模版名。
实参也叫做实际参数。
形参也是如此,定义为:
- 函数声明或定义以及异常处理中 catch 语句中的声明的对象或引用;
- 类似函数的宏的定义中紧跟在宏名后面的标识符
- 被括号包含并且使用逗号分隔的模版参数
不用记住这些,这里给出仅仅是一个参考。可以对比判断你的理解是不是全面。
编译单元
一个编译单元包含有一个或多个声明的序列。由于一个简单的编译单元可能有不止一个源文件组成,所以标准使用术语编译单元而不是源文件。一个源文件和其所有包含的头文件组成一个编译单元。亦即,完成了预处理之后的每个源文件都对应一个编译单元。
程序
程序是被连接到一起的多个编译单元。亦即,程序对应到build过程中连接之后的实体,可能是可执行文件,动态或者静态库。
左值和右值
最容易让人混淆的概念,特别是当C++0引入右值引用之后:)。记住以下几点:
1. 左值或者右值是表达式的属性。一个表达式不是左值就是右值;
2. 左值代表的是一个具名的对象(一块连续的内存),你可以使用这个名字来访问该对象;
3. 右值则代表了没有名字的对象,如字符常量,临时变量
4. 左值在需要的时候转化为右值,而不是相反。
实现定义的行为
实现定义的行为(对于形式良好的程序并且有正确的数据)依赖于特定的实现。这个行为是每一个实现者都必须明确文档说明的。例如,实现必须说明基本数据的大小,char 类型是否可以容纳负数,未捕获的异常是否导致堆栈展开等等。实行定义的行为也叫做实现依赖的行为。
未定义的行为
未定义的行为是标准没有要求的。这个定义似乎有点保守因为未定义的行为通常指示的是一种由错误的程序或错误的数据导致的状态。未定义的行为可能表现为运行时的崩溃,程序状态的不稳定和不可靠,甚至被简单忽略。超出缓存区边界写入,下标访问越过数组边界,解引用一个悬空指针或者其它类似的操作都将导致未定义的行为。
未指定的行为
未指定的行为(对于形式良好的程序并且有正确的数据)也依赖于特定的实现,但是实现者可以不必说明何种行为可能发生(当然是允许说明的)。例如,new 操作符是否要调用标准 C 库的 malloc 就是未指定的行为。
未指定的行为和实现定义的行为都是 C++标准有意没有指定的固定的行为,虽然依赖着同样的行为会导致不可移植的代码。这就允许可以在不同的平台上的简单有效地实现编译器。然而,未定义的行为应该永远避免发生。
一次定义规则
类,枚举,带有外部连接的内联函数,类模版,非静态函数模版,成员函数模版,类模版的静态数据成员,一些模版参数未指定的模版特化在满足以下条件的情况下都不能在一个程序中定义一次以上:每一个定义都出现在不同的编译单元以及定义符合下面详细描述的等价要求。
- 逐字等价性
- 语义等价性
外部连接性
可以被其它的编译单元引用或者来自本编译单元其它作用域的定义的名字有外部连接性。如external的函数或者对象名以及命名名字空间中直接包含的具有外部连接性的对象。
内部连接性
在本编译单元中定义,可以被本编译单元的其它作用域按名引用,而不能被其它的编译单元引用。如静态函数和对象,匿名联合和名字空间中的对象都只有内部连接性。
无连接性
仅仅在作用域中声明的对象没有连接性。如局部变量,除了该作用域中的对象,任何的外部对象都不能访问。
连接性在build过程的link阶段起作用,只有具有外部连接性的对象才能内其他编译单元的对象所引用。