C/C++语言的声明语法比较晦涩难懂,特翻译Accelerated C++中附录对此的解释,希望对大家有所帮助。
A.1 声明
一些声明可能难以理解,特别是如果声明几个不同类型的名字或者那些指向函数指针的函数。例如在§10.1.1/171中,我们看到
int* p, q;
定义p为一个“整型指针”类型的对象,q为一个整型对象。在§10.1.2/173中,我们看到
double (*get_analysis_ptr())(const vector<Student_info>&);
声明get_analysis_ptr为一个函数,不带任何参数,它返回一个指针,它指向一个函数,它带有一个const vector<Student_info>&参数,返回double。你可以通过重写以清楚的表达这些声明的含义,例如
int* p;
int q;
和
// 定义analysis_fp为一个函数,它带一个const vector<Student_info>&参// 数,返回一个double类型。
typedef double (*analysis_fp)(const vector<Student_info>&);
analysis_fp get_analysis_ptr();
不幸的是,这种策略不会帮助你阅读其它程序员代码中令人迷惑的声明。
一般,一个声明大致如下
声明语句:声明说明符[声明子[初始化器]] [,声明子[初始化器]]...;
它为每一个声明子声明一个名字。这些名字始于声明开始的地方终于声明作用域结束的地方。一些声明同时也是定义。名字可以声明多次,但是仅能定义一次。如果一个声明分配了存储空间或者定义了类或函数体,那么它也是一个定义。
C++继承了C的声明语法。理解声明的关键是认识到每个声明包含两个部分:一系列声明说明符,它们一起说明一个类型和其它正在声明的特性,紧跟着是零个或多个声明子(每个声明子都可选的有一个关联的初始化器)。根据说明符和声明子的形式,每个声明子都为名字赋予一个类型。
理解声明的第一步是定位说明符和声明子的边界。这很容易:所有的说明符都是关键字或者类型名,因此说明符终止于第一个不是以上类型之一的符号。例如,在
const char * const * const * cp;
很容易找到边界:double是一个类型,左括号后面既不是关键字也不是类型名。因此,声明—说明符只是double,声明子为声明的其它部分,不包含分号。
double (*get_analysis_ptr())(const vector<Student_info>&);
另一个例子,考虑§10.1.2/173中的声明:
第一个既不是关键字也不是类型名的符号是*,因此说明符是const char,唯一的声明子* const * const * cp。
A.1.1 说明符(Specifiers)
我们可以将声明—说明符分成三个部分:类型说明符,存储类说明符,和其它说明符:
声明说明符:{类型说明符|存储类说明符|其它声明说明符}
然而,这种划分仅仅有助于理解,因为声明本身不存在对应的划分:声明—说明符可以以任何次序出现。
类型说明符决定了声明的类型。我们在§A.2/299中讨论内置类型。
type-specifier: char | wchar_t | bool | short | int | long | signed
unsigned | float | double | void | type-name | const | volatile
type-name: class-name | enum-name | typedef-name
const说明符指出这种类型的对象不可以修改,volatile通知编译器变量可能以非语言定义的形式改变,应该避免优化。
注意const既可以出现在说明符部分,这样修改类型,也可以出现在声明子部分,说明一个const指针。这没有任何歧义,因为声明子部分的const总是跟着一个*。
存储类说明符决定变量的位置和生命周期:
storage-class-specifiers: register | static | extern | mutable
register说明符建议编译器通过将此对象放到寄存器中以优化性能。
一般,局部变量在退出声明它们的块(block)时即被销毁;静态变量的值在作用域的入口和出口间会被保存起来。
extern说明符表明当前的声明不是一个定义,隐含着在其它地方存在相应的定义。
mutable存储类仅用于类的数据成员,并且允许修改这些数据成员即使它们是常量对象的成员。
其它声明说明符定义了与类型无关的属性:
other-decl-specifier: friend | inline | virtual | typedef
friend说明符(§12.3.2/216 and §13.4.2/246)改写保护。
内联说明符用于函数定义,提示编译器如果可能内联下面的代码。当展开调用时,函数定义必须出现在那个作用域,因此将内联函数体放到声明函数的头文件中通常是一个好主意。
virtual说明符(§13.2.1/234)仅用于成员函数,表示这个函数是动态绑定的。
typedef说明符(§3.2.2/43)定义类型的同义词。
A.1.2 声明子(Declarators)-未完待续