变量和基本类型
1 复合类型
一条声明语句由一个基本数据类型(base type)和紧随其后的一个声明符(declarator)列表组成。
基本数据类型:int、double等;声明符:变量名、引用&、指针*(引用和指针又称为类型修饰符,为声明符的一部分)等。
2 引用
引用并非对象,只是为已经存在的对象所起的另外一个名字。
因为引用本身不是对象,所以不能定义引用的引用。
引用必须在定义的时候初始化。
3 指针
指针是一个对象,因而允许定义指针的指针。
指针无须在定义时赋初值,但如果指针没有被初始化,将拥有一个不确定的初始值。
建议初始化所有指针,并且在可能的情况下,在定义了对象之后再定义指向它的指针。
- 空指针不指向任何对象,使用字面值nullptr初始化指针。
void指针 该类型的指针所指向的地址中存放的对象的类型并不确定。
4 const限定符
const对象的值一旦创建后其值就不能再改变,所以const对象必须进行初始化。
在默认状态下,const对象仅在文件内有效,当多个文件中出现了同名的const对象时,其实等同于在不同文件中分别定义了独立的变量。当我们希望在不同文件之间使用同一const对象时(只在一个文件中定义const,而在多个文件中声明并使用它),则需要添加1
2
3
4
5
6
7
8
9
```c++
// file_1.cc定义并初始化一个常量,该常量可被其他文件访问
extern const int bufSize = fcn();
// file_1.h头文件
extern const int bufSize;// 与file_1.cc中定义的bufSize是同一个。
定义对const的引用后,不允许使用引用去改变它所绑定的对象。
4.1 指针和const
指向常量的指针
指向常量的指针不能用于改变其所指对象的值,只能使用指向常量的指针存放常量对象的地址。
1 | const double pi = 3.14; |
在初始化指向常量的指针时,既可以使用字面值进行初始化,也可以使用常量、非常量进行初始化。
和常量引用一样,指向常量的指针没有规定其所指向的对象必须是一个常量,只是限定不能使用指针(通过解引用符(操作符*))更改对象的值。
常量指针(const pointer)
指针本身是常量,const修饰符修饰的是指针本身。意味着,初始化之后指针所存储的地址值不能被改变。
1 | int errNumb = 0; |
4.2 顶层const(top-level const)
指针本身是常量,或者说对象本身是不可变的。
1 | int i = 0; |
4.3 底层const(low-level const)
指针所指向的对象是常量。
1 | const int ci = 42; |
4.4 区分顶层const和底层const的作用
- 赋值操作存在限制,不能将底层const赋值给非底层const。
1 | int num_c = 3; |
- 使用命名的强制类型转换函数const_cast时,需要能够分辨底层const和顶层const,因为const_cast只能改变运算对象的底层const。
1 | int num_e = 4; |
5 auto类型说明符
在编程时,需要把表达式的值赋给变量,则需要知道表达式的值的类型。使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
\- ```auto```定义的变量必须有初始值。
\- 同一语句中的所有变量的**初始基本数据类型**必须相同。
#### 5.1 引用与auto
使用引用的对象的类型作为auto的类型。
#### 5.2 const与auto
auto会忽略掉顶层const,而保留底层const。
### 6 decltype类型指示符
希望从表达式的类型推断出要定义的变量的类型,但是不想使用该表达式的值初始化变量,这时就无法使用```auto```,需要使用```decltype```。
#### 6.1 decltype与const
如果decltype是一个变量,则decltype会返回该变量的类型,而不会忽略任何const。
### 7 编写自己的头文件
1. 为了确保各个文件中类的定义一致,类通常被定义在头文件中,而且类所在头文件的名字应与类的名字一样。
2. 头文件通常包含那些只能被定义一次的实体,如```const constexpr```。
> 注意:头文件一旦改变,相关的源文件必须重新编译以获取更新过的声明。
#### 7.1 如何防止头文件的重复包含?
**使用预处理技术**
> **预处理器是在编译之前执行的一段程序,可以部分地改变我们写的程序。**
**头文件保护符**
头文件保护符属于预处理技术的一种,其状态依赖于预处理变量,预处理变量有两种状态:已定义、未定义。
```#define``` 指令把一个名字设为预处理变量
```#ifdef``` 当且仅当变量已定义时为真
```#ifndef``` 当且仅当变量未定义时为真
注意:当检查状态为真时,执行后续操作直到```#endif```指令为止。
使用头文件保护符防止重复包含头文件的例子:
```c++
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string.h>
struct Sales_data{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
}
在其他文件第一次包含Sales_data.h
时,程序检查#ifndef SALES_DATA_H
为真,则执行后续语句,通过#define SALES_DATA_H
定义预处理变量SALES_DATA_H
,并将Sales_data.h
的内容拷贝至当前程序中。那么当其他文件第二次包含Sales_data.h
,程序检查#ifndef SALES_DATA_H
为假,则不会执行后续语句,也就不会重复拷贝Sales_data.h
头文件。
一般将预处理变量的名称全部大写,并且应该习惯性地加上头文件保护符.