C++ - 指针、引用和const
指针和引用的相同点– 都是复合类型,实现对其他对象的间接访问。
指针和引用的不同点 –
1) 指针本身就是一个对象,允许赋值和拷贝,在生命周期内可以先后指向不同的对象;引用不是对象,只是给已存在对象绑定的一个别名,不能重新绑定到另外一个对象上。
2) 指针无须在定义时赋值,在块作用域内定义的指针如果没有被初始化将拥有一个不确定的值(和其他内置类型一样);引用必须初始化,初始化完成后将与对象一直绑定。
不能定义指向引用的对象,因为引用不是对象,没有实际地址。但可以定义对指针的引用。
int *p; int *&r = p; //r是对指针p的引用(从右向左阅读,r为引用,引用的对象是指针。)
生成空指针的几种方法(建议使用nullptr)
int *p1 = nullptr; //1. C++11引入特殊类型字面值nullptr。 int *p2 = 0; //2 int *p3 = NULL; //3. cstdlib中定义的预处理变量
void* 指针
可存放任意对象的地址,但并不知道对象的类型,所以不能直接操作所指的对象。
const对象必须初始化,一旦创建后其值不再改变。
默认情况下const对象只在当前文件内有效。当多个文件中出现同名const变量时其实等同于分别定义了多个独立的变量。
如果需要在文件间共享一个const变量(i.e. 只在一个文件中定义,在其他文件中声明并使用),则在声明和定义中都使用extern关键字,且只定义一次。
Reference to const
对常量的引用(reference to const):绑定到const对象上的引用。可以绑定到const或非const对象上(只是不允许通过该引用修改所指对象的值),但普通引用不能绑定到const对象上。
a.k.a. 常量引用 (只是简称,严格来说并不存在常量引用,因为引用不是一个对象。)
const int ci = 1024; const int &r1 = ci; r1 = 42; //错误 int &r2 = ci; //错误,非const引用不能绑定到const对象上 int i=42; cons tint &r1 = i; //正确,允许将const引用绑定到普通对象上
初始化对const的引用时允许用任意表达式作为初始值,只要该表达式结果能转换成引用的类型即可。
Pointer to const & const pointer
指向常量的指针(pointer to const):指向const对象的指针,可以指向const或非const对象(只是不允许通过该指针修改所指对象的值)。
const double pi = 3.14; const double *cptr = π *cptr = 42; //错误
常量指针(const pointer): 指针本身定位常量,将指针本身定为常量(必须初始化,初始化后值不再改变)。*放在const关键字前,说明const的是指针本身。
int errNumb = 0; int *const curErr = &errNumb; //从右向左阅读,本身是一个常量对象,类型为常量指针。 const double pi = 3.14159; const double *const pip = π //从右向左阅读,pip本身是一个常量指针,指向double常量。
顶层&底层const
顶层const (top-level const): 对象本身是常量,值不能改变。E.g. const pointer.
底层const (low-level const): 指针/引用所指对象是常量。E.g. pointer to const. 用于声明引用的const都是底层const。
int i=0; int *const p1 = &i; //顶层, pi的值不能改变 const int ci = 42; //顶层,ci的值不能改变 const int *p2 = &ci; //底层,p2的值允许改变 const int * const p3 = p2; //左边的const为底层,右边的const为顶层 const int &r = ci; //底层
顶层const执行拷贝操作时不受影响,因为不会改变被拷贝对象的值;
底层const执行对象拷贝操作时要求拷入拷出对象必须具有相同的底层const资格。(等号右边的非const可以转换为const,但反之不行。 )
int *p = p3; //错误,p3包含底层, p没有 p2 = p3; //正确,p2和p3都是底层 p2 = &i; // 正确,int*能转换为const int*
const expression
常量表达式(const expression): 值不会改变并且在编译过程中就能得到计算结果的表达式。E.g. 字面值;用常量表达式初始化的const对象。
一个对象/表达式是否为常量表达式由数据类型和初始值共同决定。
const int max_files = 20; // max_files是 const int limit = max_files+1; // limit是 int staff_size = 27; // 因为数据类型非const, staff_size不是 const int sz = get_size(); // 因为具体值要到运行时才能获得,sz不是
C++11允许将变量声明为constexpr类型,声明为constexpr的变量一定是常量且必须用常量表达式初始化。允许定义一种constexpr函数用于初始化constexpr变量,使之在编译时就可以计算结果。
constexpr int mf = 20; const expr int limit = mf+1; const expr int sz = size(): // 只有当size是一个constexpr函数时才是一条正确声明
如果认定一个变量时一个常量表达式,就可以把它声明为constexpr类型。
constexpr指针的初始值必须是nullptr或0,或存储于某个固定地址中的对象(定义于所有函数体以外的对象,或有效范围超出函数本身的变量)。
如果在constexpr声明中定义了一个指针,constexpr仅对指针有效,与所指对象无关,既可指向const也可指向非const。
const int *p = nullptr; //指向int常量的指针 constexpr int *q = nullptr; //指向int的常量指针,顶层const