类型别名
程序越复杂,程序的类型也会越复杂。类型复杂体现在两点:
1、 难于拼写,且不能体现其真实目的和含义。
2、 程序员不了解程序的情况下,无法确定需要声明什么样的类型。
类型别名可使程序的类型名字变得简单明了、易于理解和使用。类型别名需要使用typedef关键字,如:
1 typedef double wages; //wages和double是同义词。wages含义和目的比double更明确,也更易于程序员在不了解程序的情况下,确定什么样的变量需要声明为wages。 2 typedef wage base , *p; //base是double的同义词,p是double*的同义词。
C++11新标准可使用using关键字声明类型别名,如:
1 using wages = double; //wages是double的同义词,等价于typedef double wages;
类型别名和类型的名字等价,只要是类型的名字能出现的地方,就能使用类型别名。
另外,使用类型别名声明const的指针时,需要注意理解下面第三行代码:
1 int i = 0; 2 using pint = int *; 3 const pint ci = &i; //ci是指向int的常量指针,而非指向常量int的指针。
特别注意的是pint不能简单的替换成int *,理解成:
1 const int *ci = &i; //这里ci是指向常量int类型的指针,而非指向int的常量指针。
为了解释这种情况,我们可以这样理解:声明语句中用到的pint,其基本数据类型是指针。可是替换成int *重写声明语句后,数据类型变成了int,*成了声明符的一部分,这样的结果是:const int成了基本数据类型,指针ci指向const int;而当pint是基本数据类型时,基本类型就是一个指针,const pint表示指针是常量,所以ci就是常量指针,它指向int数据类型。
auto类型说明符
在把表达式的值赋给变量,就要求声明变量的时候清楚知道表达式的类型。然而要做到这一点并非那么容易,有时根本做不到。为了解决这个问题,C++11新标准引入了auto类型说明符。
1 auto item = val1 + val2; //由val1和val2相加的结果可以推断出item的类型
auto能让编译器替我们去分析表达式所属的类型。通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值。 使用auto也能在一条语句中声明多个变量:
1 auto i = 0, *p = &i; //正确 2 auto sz = 0, pi = 3.14; //错误:sz和pi的类型不一致。
需要注意的是auto一般会忽略掉顶层const(声明指向常量变量的常量指针时:const char * const pc = 0,第一个const是底层,第二个const是顶层),同时底层const会保留下来,如:
1 const int ci = i, &cr = ci; 2 auto b = ci; //b是一个整数(ci的顶层const特性被忽略掉了) 3 auto c = cr; //c是一个整数(cr是ci的别名,ci本身是一个顶层const) 4 auto d = &i; //d是一个整形指针 5 auto e = &ci;//e是一个指向整数常量的指针(对常量对象取地址是一种底层const)
如果希望推断出的auto类型是一个顶层const,需要明确指出:
1 const auto f = ci;
另外,还可以将引用类型设为auto:
1 auto &g = ci; //g是一个整形常量的引用,绑定到ci。
有时我们希望从表达式的类型来定义变量的类型,但是不想用该表达式的值初始化变量。可以使用decltype类型指示符。
1 decltype(f()) sum = x; //sum的类型就是函数f的返回类型。
编译器并不实际调用函数f,而是当调用发生时,使用f的返回值类型作为sum的类型。
decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型,包括顶层const和引用在内。如:
1 const int ci = 0, &cj= ci; 2 decltype(ci) x = 0; //x的类型是const int。 3 decltype(cj) y = x; //y的类型是const int &, y绑定到变量x。 4 decltype(cj) z; //错误:z是一个引用,必须初始化。
decltype和引用:
当decltype使用的表达式不是变量,则返回表达式结果对应的类型。而且有些表达式将向decltype返回一个引用类型:
1 int i = 42, *p = &I, &r = i; 2 decltype(r + 0) b; //正确:加法的结果是int,因此b是一个未初始化的int。 3 decltype(*p) c; //错误:c是int &,必须初始化。
因为r是一个引用,因此decltype(r)的结果也是引用类型,而表达式r+0的结果显然是一个具体的值,而非一个引用。另一方面,如果表达式的内容是解引用操作,则decltype也将得到引用类型。因为解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此,decltype(*p)的结果类型就是int &,而非int。
decltype和auto的另一处重要区别是,decltype的结果类型和表达式的形式密切相关。如:
1 decltype((i)) d; //错误:d是int &,必须初始化。 2 decltype(i) e; //正确:e是一个(未初始化的)int。
上述代码中,如果i变量不加括号,得到的结果是该变量的类型。如果i变量加括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。