第二章——变量和基本类型
说在前面
C++这个板块是我在学习了C之后才学的,因此我只会记录一些重要的或者在C中没有的东西
C++板块是基于C++primer的方案学习的
1、基本内置类型
bool类型取值只为真(1)和假(0)
当一个算术表达式中既有无符号数(unsigned)又有int型时,int型的值会转换成无符号数。且值大于0时会正常显示,小于零时会异常显示(当int为4字节时,值为2^32 + 计算结果(负数))也就是4294967295加上
(-n)。
指定字面值类型:
- 字符和字符串字面值
| 前缀 | 含义 | 代表类型 |
|---|---|---|
| u | Unicode16字符 | char16_t |
| U | Unicode32字符 | char32_t |
| L | 宽字符(2字节) | wchar_t |
| u8 | Unicode8字符 | char |
- 整型字面值
| 后缀 | 最小匹配类型 |
|---|---|
| u or U | unsigned |
| l or L | long |
| ll or LL | long long |
- 浮点型
| 后缀 | 代表的类型 |
|---|---|
| f or F | float |
| l or L | long double |
2、变量
一种初始化字符串的方法
1
std::string a("test");
字符串变量名a,内容为test
3、对象
通常情况下,对象是指一块能存储数据并具有某种类型的内存空间
关于初始化的问题
在学C的时候我自己也是一直把初始化理解成赋值,但是也一直感觉怪怪的,特别是对于指针变量的初始化,感觉明显是不符合“赋值”这一操作的,今天在阅读书籍时书中也指出了初始化≠赋值,但是具体的区别目前我也不能讲的很明白,学到后面如果搞懂了再说吧。
- 定义在函数体内部的变量将不被初始化。
列表初始化
用花括号来初始化变量,在C++中以下四种初始化都是正确的:
1 | int a =1; |
4、标识符
虽然感觉很怪异,但是int _=1;是合法的。
5、复合类型变量
引用
为对象起另外一个名字,同时引用必须被初始化。
1 | int a(1); |
- 引用并非对象,可以只是理解为一个已存在变量的别名。
- 一旦使用引用,引用对象将和引用的初始化变量绑定,无法更改,因此引用必须初始化。
- 引用只能绑定在对象上,而不能与某个字面值或某个计算结果绑定。
指向指针的引用:
1 | int i=1; |
指针
规则与C基本一样,不再赘述。
6、const 限定符
作用:定义一个变量后,它的值不能再被改变
因此,const的对象必须初始化。
当多个文件出现了同名的const变量时,视作分别定义了独立的变量,也就是说默认情况下const对象只在本文件内有效。(当然也可以使用extern关键字,具体可见一些关键字 | 小董的BLOG (gitee.io))
对于使用extern,如果要在多个文件之间共享const对象,则在定义该对象时也必须添加extern
对const对象的引用
1 | const int a=10; |
假设第三行操作合法,则可以通过b2改变a的值,这显然是不正确的
若初始化引用的对象为表达式,只要该表达式的结果能转化成引用的类型即可
1
2
3
4
5
6
7
8
9int a=10;
const int &b=a;
//允许将const int型引用绑定到普通的int对象上(其他普通变量也可以视情况转化)
//因此,可以有以下操作
int a=10;
int &b1=a;
const int &b2=a;
//可以通过b1改变a的值,但不允许通过b2改变a的值
指向const对象的指针和const指针
1 | //指向const对象的指针 |
说明一下,上述中的int *p=&a;也可以改为const int *p=&a;且二者完全一致,原因与后文中的顶/底层const有关
1 | //const指针 |
将指针本身定义为const型(不太好用语言描述感觉),即一旦初始化完成,该指针便无法再指向其他对象。
下面还是一个const指针的说明:
1 | //const指针 |
当使用const指针指向的值也是const类型时,const指针本身必须再次使用一次const,因为要保持类型的一致
顶/底层const
对于指针,指针本身是一个对象,而它也指向了另一个对象,因此用顶层const表示指针本身是个const类型,用底层const表示指针所指的对象时一个const型。一般来说,顶层const可以表示任意对象。
当const + 类型 时,意为使这个类型的值为常量,如
1
const int *p;
定义一个指针变量,该变量所指的值为int型,因此该值(*p)不可被修改,但是p可以被修改。
当const + 变量 时,意为使该变量为常量,如:
```c++
int* const p;1
2
3
4
5
6
7
8
9
10
11
12
指针变量p为常量,因此其指向的地址无法被改变,但是地址中的值(*p)可以被改变。
## 7、constexpr类型
* 常量表达式:指值不会改变**并且在编译过程就能得到结果的表达式**;字面值、用常量表达式初始化的const对象也是常量表达式。
```c
const int i = 10; //字面值是常量表达式
const int j = i+1; //j是常量表达式
int k = i; //k不是const对象,所以k不是是常量表达式
const int m = get_val(); //m的值直到运行时才能获得,所以m不是常量表达是从上面我们可以看出,使用const进行声明时,我们需要人为的验证赋给const对象的初始值是不是常量表达是,在复杂系统中,有时候很难分辨,而使用constexpr进行声明时,可以由编译器来验证变量是不是常量表达式
1 | constexpr int func(int n) { |
const和constexpr的区别
- 对于修饰对象来说,const并未区分出编译期常量和运行期常量,constexpr限定在了编译期常量
- 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。
- constexpr将它所定义的对象置为顶层const
1 | const int*p1=nullptr; //p1是一个指向常量的指针 |
8、处理类型
(1)类型别名
类型别名是一个名字,是某种类型的同义词。有两种办法可用于定义类型别名:
使用关键字typedef,详见一些关键字 | 小董的BLOG
使用别名声明:
1
using INT =int;
类型别名与指针、常量
有以下代码:
1 | using abc=char*; |
感觉可以直接理解为这里的const为顶层const型
(2)auto类型说明符
这里auto和C中用法有所出入,在C++11新标准中,auto用于让编译器通过初始值来推算变量的类型
1 | int a=1,b=2; |
此时c的类型就为int
- auto定义的变量必须有初始值
- 参与初始化的各对象类型必须相同,否则会报错
在复合类型与常量参与的情况下:
1 | const int a=1; &a1=a; |
auto一般会忽略顶层const
对一个常量对象取地址是一种底层const
(3)decltype类型指示符
当我们定义一个变量时希望用一个表达式返回的类型定义该变量,但是又不想用该表达式的结果的值初始化变量,为满足这一要求,C++11新标准引入了decltype,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式的类型,却不实际计算它的值。
1 | int a=1; |
decltype()内的参数可以为变量、函数、甚至引用
decltype和引用:
1 | int a=1,&a1=a,*p=&a; |
当decltype传入的参数添加上一对以上的括号后,例如decltype((a)),结果永远为引用