目录
引用
引用的特性
引用的使用
const 引用
权限缩小(合法)
权限放大(非法)
引用
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
类型& 引用别名=引用对象;
// C++ 标准对 main() 函数有特殊规定:main() 函数是唯一允许不写 return 语句的函数。
引用的特性
引用在定义时必须初始化。
一个变量可以有多个引用。
引用一旦引用一个实体,就不能再引用其他实体。
//引用的功能 //1.做函数的形参,修改形参影响实参 using namespace std; void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } ////2.做函数形参,减少拷贝,提高效率 struct A { int a[100]; int b; // //... }; // void func(struct A* aa) {} //// 引用功能2:做函数形参,减少拷贝,提高效率 void func(struct A& aa) {} int main() { int x = 1, y = 2; cout << x << " " << y << endl; swap(x, y); cout << x << " " << y << endl; struct A a; // //类型struct A 函数名为a func(&a); func(a); return 0; }引用的使用
引用在实践中主要用于引用传参和引用做返回值中,以减少拷贝、提高效率,并在改变引用对象时同时改变被引用对象。
引用传参与指针传参功能类似,但引用传参相对更方便一些。
引用返回值的场景相对比较复杂,我们在这里简单介绍了一下场景,还有一些内容在后续类和对象章节中会继续深入讲解。
引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++ 的引用与其他语言的引用(如 Java)有很大区别,除了用法,最大的区别是 C++ 引用定义后不能改变指向,而 Java 的引用可以改变指向。
一些主要用 C 代码实现的数据结构教材中,使用 C++ 引用替代指针传参,目的是简化程序,避开复杂的指针,但是很多同学没学过引用,导致一头雾水。
int func() { int ret; return ret; } ////返回的是拷贝的临时变量 int& SLAT(SL& ps, int i) { assert(i < ps.size); return ps.arr[i]; } ////传引用返回 返回的是别名 并没有创建临时对象 可以对其进行修改const 引用
可以引用一个 const 对象,但是必须用 const 引用。const 引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。
引用权限只能缩小、不能放大
权限缩小(合法)
普通对象可以绑定const 引用:
int a = 10; const int& ra = a; // 合法原对象a:可读、可修改ra:只能读,不能改
相当于访问权限收窄,C++ 允许。
权限放大(非法)
const对象不能绑定普通非const引用:
const int a = 10; int& ra = a; // 编译报错!原对象
a:只读ra允许读写权限变大,破坏常量的不可修改特性,直接禁止。
需要注意的是,类似int& rb = a*3;或double d = 12.34; int& rd = d;这样的场景中,a*3的结果保存在一个临时对象中,int& rd = d也是类似,在类型转换中会产生临时对象存储中间值。也就是说,rb和rd引用的都是临时对象,而 C++ 规定临时对象具有常性,所以这里就触发了权限放大,必须用常引用才可以。
临时对象的特殊性:自带常性
临时对象是编译器自动创建、没有名字、生命周期极短的中间变量(表达式结果、类型转换中间值),C++ 规定所有临时对象都是 const 性质。
所谓临时对象,就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++ 中把这个未命名对象叫做临时对象。
表达式运算产生临时:
int a = 2; int& rb = a * 3; // 报错int main() { int x = 0; int& r1 = x; // // 权限可以缩小 // //r1是x的别名 const int& r3 = x; // //r3缩小权限 const int y = 1; // // 权限不能放大 int& r2 = y; const int& r2 = y; // // // 权限有没有放大?没有 int z = y; // // // 指针和引用才涉及权限缩小和放大 const int* p1 = &y; // // 权限不能放大 int* p2 = p1; const int* p2 = p1; int* p3 = &z; // // 权限可以缩小 const int* p4 = p3; // // // // return 0; }这是一些记忆的零碎知识点:
传值传参调用拷贝构造——
后定义的先析构。
如果要显式地写析构,就要写拷贝构造。