别名
定义别名有两种方法,一种是从C语言沿用下来的typedef
关键词, 还有一种为C++11重新加入的using
来对别名进行声明
用法:
typedef double wages;
typedef wages base, *p;
typedef struct TNode {
int data;
struct TNode *left, *right;
} TNode, *TreeNode;
// using 的使用
using wages = double;
using TreeNode = struct TNode {
int data;
struct TNode *left, *right;
};
注意点
对于复合类型而言,会有一些意想不到的后果
typedef char *pstring;
char c = 'a', d = 't';
const pstring cstr_1 = &c;
const char* cstr_2 = &c;
对于上述语句,const pstring cstr_1 = &c
定义了一个指向char类型的常量指针,即不能改变指针指向的值,即cstr_1 = &d
会报错;
而const char* cstr_2 = &c
则定义了一个指向常量的指针,即无法通过指针来对指向对象进行修改,即*cstr_2 = d
会报错, 但cstr_2 = &d
可以顺利进行。
类型说明符
非常好用的auto
声明符?
C++11中引入的auto
声明符允许C++编译器来帮助我们进行类别的分析,用法十分简单,但也需要注意几个点:
- 对于写在同一条声明语句中的代码,必须有着相同的基本数据类型。
- 对于
const
关键字而言,若用auto进行分析,则会忽略顶层const
,保留底层const
以下用具体代码说明
int a = 10;
auto b = a, *p = &a; // 正确写法,a是int类型, p是指向int类型的指针,因此为相同的基本数据类型
auto b = 3.14, *p = &a; // 错误写法, b是double类型, 而p是指向int类型的指针, 数据类型不相同
对于第二点,先回顾下顶层const
和底层const
, 首先顶层const
和底层const
只在const
作用于指针时会有所区别,直接作用于目标对象时没有区别(例如const int
),可以把这类对象默认为顶层const
; 其次在指针中,当const
限定的对象为指针时,其为顶层const
;否则为底层const
。
const int a = 10; // 默认为顶层const
auto b = a; // b为int类型, 但auto忽略了const属性
b = 7;
cout << b;
output: 7
const int a = 10, *p = &10; // 指向常量的指针, 为底层const
auto ptr = p; // ptr依旧是指向常量的指针,并且保留了底层const的特性
cout << *ptr;
output: 10
// 以下语句运行错误,由于ptr是指向常量的指针,不可通过ptr改变对象的值
*ptr = 20;
// p是常量指针,const修饰指针,故为顶层const,p指向的对象不可改变
int* const p = &a;
auto ptr = p; // p是指向int的指针 auto忽略了const 因此ptr的值可以改变
int b = 50;
ptr = &b;
cout << ptr;
output: 50
// 对于引用
int a = 10, &b = a;
auto c = b; // c为int类型
decltype
类型指示符
引用书上的原话
可能书上的描述有些隐晦,稍微解释一下。 auto
和decltype
的区别类似const
和constexpr
。
- 对于
auto
声明符而言, 对类型的推断是在编译时候完成的,编译器会尝试计算一次变量的值,并用这个值初始化auto
所声明的对象, 例如auto a = foo()
, 编译器在编译时就会把foo()
的值计算出来,并赋值给变量a
; - 对于
decltype
类型指示符而言,编译器并不会计算function()
的值,而是分析function
来推断变量类型,由于不会计算值,所以不会在编译阶段给变量赋值,该操作全部在运行时进行。
这也是为什么在有auto
的前提下引入decltype
类型指示符的原因,因为在编译时有时我们并不打算在编译的机器上得到这个变量的值(或者在编译机器上有各种限制,比如无法连接数据库等等)。
decltype
语法
decltype(target_type) name = init_value;
其中target_type
是用来推断的数据类型,可以是常量也可以是表达式/函数
在const
的处理上上decltype
与auto
的区别
前面已经提到,const
对于顶层const
是直接忽略的,对于底层const
是保留的;而decltype
则有所不同。
- 对于低层
const
,decltype
依旧是保留,这一点与auto
没有区别 - 对于顶层
const
和引用类型,若target_type
为常量,则其保留常量中所有的限制; 若target_type
不是常量,而是表达式/函数,则其丢弃所有顶层const
的特性
以下通过代码说明
- 常量 ```cpp const int a = 10, &b = a; // a和引用b均为顶层const decltype(a) x = 0; // x保留a的限制,等同于 const int x = 0; x = 10; // 报错 decltype(a) y = a; // y类似于为const int y = x; decltype(b) z = x; // z类似于 const int &z = x; z为常量引用类型
//底层const情况 const int* p = &a; decltype(p) ptr = &a; // ptr 保留底层const所有特性, 即p为指向常量的指针 `` 注:上面代码中,由于
z为引用类型,由于引用必须被初始化的特性,
z`必须被初始化。
- 表达式/变量
const int a = 10, &b = a;
decltype(a) x = 0; // 上述可知对x的定义等同const int x = 0;
decltype(a + 0) y = 0; // a + 0是表达式,因此y丢失了所有的顶层const情况
decltype(b + 0) z = 0; // b + 0是表达式,z丢失了引用的信息
// 与 int y = 0; int z = 0;是一样的
y = 20; // 正确运行
decltype
的特殊点- 对于解引用符,得到的是引用类型
- 对于
decltype((variable))
, 得到的类型是引用类型
const int a = 10, &b = a, *p = &a;
decltype(*p) c; // 错误, c是引用类型,必须初始化
decltype(*p) c = a; // 正确, c是引用类型并且初始化了
decltype(b) d; //错误, d是引用类型,必须初始化
decltype(b) d = a; // 正确, d是引用类型并且初始化了
decltype(a) e; // 没有括号时正确, e是int类型, 可以不进行初始化
decltype((a)) e; //错误, e是引用类型,必须初始化
decltype((a)) e = a; // 正确, e是引用类型并且初始化了
注:以上例子中引用类型为int&