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