|
1 | | -# 从 C 到 C++ |
2 | | - |
3 | | -### 变量定义中`*`和`&`的含义? |
4 | | - |
5 | | -**Q:** `int *a1, a2;`中只有`a1`被定义为了`int*`,请问对于`int &a, b;`有没有相同的性质?对于下面的例子,用`intPtr`为何不会有类似问题? |
6 | | - |
7 | | -```c++ |
8 | | -using intPtr = int*; |
9 | | -intPtr p, q; |
10 | | -``` |
11 | | - |
12 | | -**A:** 第一个问题的回答是:**是**,C++ 的引用声明符具有和 C 的指针声明符类似的语法。即 `int &a{b}, c{d};` 中,`c` 不是 `d` 的引用,而是从 `d` 初始化的 `int` 类型变量。 |
13 | | - |
14 | | -> 在 C 中,指针声明符、数组声明符、函数声明符等语法规则是参照解地址表达式、下标表达式和函数调用表达式的“形式”而设计的,比如 `int *p, **q` 隐含 `*p` 表达式和 `**q` 表达式都将具有 `int` 类型。但是这种设计偏好非常古怪,我们不建议如此使用。在 C++ 中,我们强烈建议每条声明只引入一个标识符,如 `int* p;` 或 `int& r = a;`,并将指针声明符、引用声明符靠左对齐以体现 `p` 或 `r` 的类型。 |
15 | | -> |
16 | | ->> P.S. 不存在“引用的引用”类型,`int &r{...}, &&r2{...};` 中的 `&&` 是另一个语法“右值引用”。 |
17 | | -
|
18 | | -对于第二个问题,`using intPtr = int*;` 所表达的含义是将 `intPtr` 作为 `int*` 类型的别名。**这并非文本替换的过程**。除了你说的 `intPtr p, q;` 将 `p` 和 `q` 都声明为 `intPtr` 类型,以下的例子也可供研究: |
19 | | - |
20 | | -```cpp |
21 | | -using intPtr = int*; |
22 | | -const intPtr ptr{}; // ptr 是 int* const 类型,为什么? |
23 | | - |
24 | | -using constInt = const int; |
25 | | -constInt* ptr2{}; // 它的类型又是什么? |
26 | | -const constInt* ptr3{}; // 这个呢? |
27 | | -``` |
28 | | - |
29 | | -### `delete` 如何使用? |
30 | | - |
31 | | -**Q:** `delete` 内存空间应该在变量完全使用完后进行吗?是不是被 `delete` 掉之后这个变量就不再存在于程序中并且不可访问了? |
32 | | - |
33 | | -**A:** 可以这么理解。但是要注意的是,只有通过 `new` 手动申请的内存空间需要 `delete` 进行释放。释放内存是告知系统,对应的 内存空间不再需要了。下面的例子中,`delete a` 之后,`a` 指向的内存空间就不能再被使用了,但是 `a` 本身仍然存在,储存着之前分配的内存地址。这里一定要进行妥善处理,不能继续使用 `a` 指向的内存,否则将导致 "use-after-free" 错误。 |
34 | | - |
35 | | -```c++ |
36 | | -int *a = new int; |
37 | | -*a = 5; |
38 | | -delete a; |
39 | | -std::cout << a << std::endl; // a仍然存在,但其指向的内存已不可用 |
40 | | -// std::cout << *a << std::endl; // 错误行为(未定义行为) |
41 | | -``` |
42 | | - |
43 | | -### 重载函数的调用原则是什么? |
44 | | - |
45 | | -**Q:** 函数重载中,隐式类型转换发生的情形有哪些? |
46 | | - |
47 | | -**A:** C++ 语言标准规定,在存在多个同名的重载函数时,会进行**重载决议**,从而挑选出**唯一**的最佳可行函数。 |
48 | | - |
49 | | -重载决议的步骤较为复杂,简单来看核心原则是:**尽可能少得进行不符合直觉的隐式类型转换**。 |
50 | | - |
51 | | -严谨来说:C++ 语言标准中规定了隐式类型转换的分级,从而确定了不同隐式类型转换的优劣。在调用被重载的函数时,首先会找出所有可行函数,然后对每个可行函数的调用进行排名,如果调用某个函数 `F1` 的隐式类型转换存在一个实参优于调用同名重载函数 `F2` ,那么便会调用函数 `F1` 。此外,如果在此步出现相同的排名情况,C++ 语言标准也规定了后续的比较原则,在此不再赘述。 |
52 | | - |
53 | | -如果某函数调用无法被重载决议选择,那么不允许调用。 |
54 | | - |
55 | | -### 复杂类型变量如何方便地声明? |
56 | | - |
57 | | -**Q:** 如果要定义一个指向函数的指针数组,能否使用 `auto` 或者 `using` 来帮助我减少声明时的麻烦? |
58 | | - |
59 | | -**A:** `auto` 关键字不能用于数组的声明,但你可以利用 `using` 关键字进行声明,参照下列示例代码: |
60 | | -```c++ |
61 | | -using Func = int(int); |
62 | | -using FuncPtr = Func*; |
63 | | -FuncPtr f[5]; |
64 | | -// f为一个函数指针数组,含有5个元素 |
65 | | -// 每个元素都是指向int(int)类型的函数指针 |
66 | | -``` |
| 1 | +# 从 C 到 C++ |
| 2 | + |
| 3 | +### 变量定义中`*`和`&`的含义? |
| 4 | + |
| 5 | +**Q:** `int *a1, a2;`中只有`a1`被定义为了`int*`,请问对于`int &a, b;`有没有相同的性质?对于下面的例子,用`intPtr`为何不会有类似问题? |
| 6 | + |
| 7 | +```c++ |
| 8 | +using intPtr = int*; |
| 9 | +intPtr p, q; |
| 10 | +``` |
| 11 | + |
| 12 | +**A:** 第一个问题的回答是:**是**,C++ 的引用声明符具有和 C 的指针声明符类似的语法。即 `int &a{b}, c{d};` 中,`c` 不是 `d` 的引用,而是从 `d` 初始化的 `int` 类型变量。 |
| 13 | + |
| 14 | +> 在 C 中,指针声明符、数组声明符、函数声明符等语法规则是参照解地址表达式、下标表达式和函数调用表达式的“形式”而设计的,比如 `int *p, **q` 隐含 `*p` 表达式和 `**q` 表达式都将具有 `int` 类型。但是这种设计偏好非常古怪,我们不建议如此使用。在 C++ 中,我们强烈建议每条声明只引入一个标识符,如 `int* p;` 或 `int& r = a;`,并将指针声明符、引用声明符靠左对齐以体现 `p` 或 `r` 的类型。 |
| 15 | +> |
| 16 | +>> P.S. 不存在“引用的引用”类型,`int &r{...}, &&r2{...};` 中的 `&&` 是另一个语法“右值引用”。 |
| 17 | +
|
| 18 | +对于第二个问题,`using intPtr = int*;` 所表达的含义是将 `intPtr` 作为 `int*` 类型的别名。**这并非文本替换的过程**。除了你说的 `intPtr p, q;` 将 `p` 和 `q` 都声明为 `intPtr` 类型,以下的例子也可供研究: |
| 19 | + |
| 20 | +```cpp |
| 21 | +using intPtr = int*; |
| 22 | +const intPtr ptr{}; // ptr 是 int* const 类型,为什么? |
| 23 | + |
| 24 | +using constInt = const int; |
| 25 | +constInt* ptr2{}; // 它的类型又是什么? |
| 26 | +const constInt* ptr3{}; // 这个呢? |
| 27 | +``` |
| 28 | + |
| 29 | +### `delete` 如何使用? |
| 30 | + |
| 31 | +**Q:** `delete` 内存空间应该在变量完全使用完后进行吗?是不是被 `delete` 掉之后这个变量就不再存在于程序中并且不可访问了? |
| 32 | + |
| 33 | +**A:** 可以这么理解。但是要注意的是,只有通过 `new` 手动申请的内存空间需要 `delete` 进行释放。释放内存是告知系统,对应的 内存空间不再需要了。下面的例子中,`delete a` 之后,`a` 指向的内存空间就不能再被使用了,但是 `a` 本身仍然存在,储存着之前分配的内存地址。这里一定要进行妥善处理,不能继续使用 `a` 指向的内存,否则将导致 "use-after-free" 错误。 |
| 34 | + |
| 35 | +```c++ |
| 36 | +int *a = new int; |
| 37 | +*a = 5; |
| 38 | +delete a; |
| 39 | +std::cout << a << std::endl; // a仍然存在,但其指向的内存已不可用 |
| 40 | +// std::cout << *a << std::endl; // 错误行为(未定义行为) |
| 41 | +``` |
| 42 | + |
| 43 | +### 重载函数的调用原则是什么? |
| 44 | + |
| 45 | +**Q:** 函数重载中,隐式类型转换发生的情形有哪些? |
| 46 | + |
| 47 | +**A:** C++ 语言标准规定,在存在多个同名的重载函数时,会进行**重载决议**,从而挑选出**唯一**的最佳可行函数。 |
| 48 | + |
| 49 | +重载决议的步骤较为复杂,简单来看核心原则是:**尽可能少得进行不符合直觉的隐式类型转换**。 |
| 50 | + |
| 51 | +严谨来说:C++ 语言标准中规定了隐式类型转换的分级,从而确定了不同隐式类型转换的优劣。在调用被重载的函数时,首先会找出所有可行函数,然后对每个可行函数的调用进行排名,如果调用某个函数 `F1` 的隐式类型转换存在一个实参优于调用同名重载函数 `F2` ,那么便会调用函数 `F1` 。此外,如果在此步出现相同的排名情况,C++ 语言标准也规定了后续的比较原则,在此不再赘述。 |
| 52 | + |
| 53 | +如果某函数调用无法被重载决议选择,那么不允许调用。 |
| 54 | + |
| 55 | +### 复杂类型变量如何方便地声明? |
| 56 | + |
| 57 | +**Q:** 如果要定义一个指向函数的指针数组,能否使用 `auto` 或者 `using` 来帮助我减少声明时的麻烦? |
| 58 | + |
| 59 | +**A:** `auto` 关键字不能用于数组的声明,但你可以利用 `using` 关键字进行声明,参照下列示例代码: |
| 60 | +```c++ |
| 61 | +using Func = int(int); |
| 62 | +using FuncPtr = Func*; |
| 63 | +FuncPtr f[5]; |
| 64 | +// f为一个函数指针数组,含有5个元素 |
| 65 | +// 每个元素都是指向int(int)类型的函数指针 |
| 66 | +``` |
0 commit comments