【C++入门】缺省参数、函数重载与引用(上):https://developer.aliyun.com/article/1496840
3.引用
3.1引用概念
- 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
- 它允许我们使用一个变量名来引用另一个变量的值,而不是创建一个新的变量
- 引用通常用于传递函数的参数、返回函数值和简化语法
在C++中,我们可以通过以下方式定义一个引用:
type &ref = variable;//类型& 引用变量名(对象名) = 被引用实体
例如:
void TestRef() { int a = 10; int& ra = a;//<====定义引用类型 printf("%p\n", &a); printf("%p\n", &ra); }
注意:引用类型必须和引用实体是同种类型的
再举个例子:
可以看到它们都是同一个地址,指向同一个变量
#include <iostream> int main() { int num = 10; int &ref = num; // 引用num变量 std::cout << "num: " << num << std::endl; std::cout << "ref: " << ref << std::endl; num = 20; std::cout << "num: " << num << std::endl; std::cout << "ref: " << ref << std::endl; ref = 30; std::cout << "num: " << num << std::endl; std::cout << "ref: " << ref << std::endl; return 0; }
结果如下:
num: 10 ref: 10 num: 20 ref: 20 num: 30 ref: 30
可以看到,无论我们通过num还是ref来修改变量的值,都会影响到另一个变量的值,因为它们实际上是同一个变量的不同名称。
3.2引用特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
void TestRef() { int a = 10; // int& ra; // 该条语句编译时会出错 int& ra = a; int& rra = a; printf("%p %p %p\n", &a, &ra, &rra); }
结果如下:
3.2常引用
C++中的常引用有两种情况:
- const引用:使用const关键字来修饰引用,表示引用的值不可修改。例如:
int x = 10; const int& ref = x;
在上面的例子中,ref是一个对x的常引用,意味着不能通过ref来修改x的值。
- 常对象的引用:当引用一个常对象时,引用也必须是常引用。例如:
const int x = 10; const int& ref = x;
在上面的例子中,ref是对常对象x的常引用。
常引用的作用是为了在不修改值的情况下使用对象,同时可以避免不必要的复制。常引用经常用于函数参数中,以便避免对实参进行复制。
指针和引用进行赋值和初始化时,权限可以缩小,但是不能放大
例如:
const int x = 10; int& ref = x;//这是错误的,它放大了权限
在上面的例子中,原本的x被const修饰不能被改变数据,但是ref引用它时没有用const修饰说明可以被改动,放大了权限是不被接受的;这和指针是类似的:const int* p1 = NULL; int* p2 = p1;//这也是错误的
3.3使用场景
- 做参数
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
结果如下:
可以看到我们没有使用传递变量的指针给函数就改变了实参的数据;
使用引用作为函数参数可以避免复制大量的数据,提高函数的效率。同时,通过引用传递参数可以实现对原始数据的修改,而不需要借助指针来实现。
- 做返回值
引用作为函数的返回值前提是:返回的值在调用完函数后不会被释放销毁
例如:
int& Count() { static int n = 0; n++; return n; }
n用static修饰为静态全局变量即使函数调用结束也不会被释放,所以可以用引用作为函数的返回值,这样就不需要再临时拷贝一份,减少了空间的消耗;
那么作为函数的返回值有什么作用呢?
举个例子:
#include<iostream> #include<assert.h> #define N 10 using namespace std; //顺序表 typedef struct AY { int arr[N]; int size; }AY; int& PosAt(AY& ay, int x) { assert(x < N); return ay.arr[x]; } int main() { AY ay; for (int i = 0; i < N; i++) { PosAt(ay, i) = i;//利用引用返回修改对应的值 } for (int i = 0; i < N; i++)//打印 { cout << PosAt(ay, i) << " "; } cout << endl; return 0; }
结果如下:
从上面的函数我们可以知道引用返回除了可以减少拷贝还可以修改返回的对象;
总结:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
3.4引用和指针的区别
(1)在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间;而指针是保存着变量的地址的,是有独立的空间的;
例如:
int main() { int a = 10; int& ra = a; cout<<"&a = "<<&a<<endl; cout<<"&ra = "<<&ra<<endl; return 0; }
结果如下:
通过上述例子我们发现引用和原来的变量的地址是一样的;
(2)在底层实现上实际是有空间的,因为引用是按照指针方式来实现的;
例如:
#include<iostream> using namespace std; int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20; return 0; }
通过上述例子,我们调试查看反汇编,发现引用和指针操作底层逻辑是一样的;
(3)引用和指针的不同:
- 引用概念上定义一个变量的别名,指针存储一个变量地址;
- 引用在定义时必须初始化,指针没有要求;
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体;
- 没有NULL引用,但有NULL指针;
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节);
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小;
- 有多级指针,但是没有多级引用;
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理;
- 引用比指针使用起来相对更安全;
4.结语
以上就是C++中缺省参数、函数重载以及引用的所有内容啦 ~,缺省参数函数重载以及引用的出现是为了补充C语言语法的不足以及对C语言设计不合理的地方进行优化,引用的出现大大降低了我们学习C语言时相对于指针的难度,也便于我们更好的理解和使用,完结撒花 ~🥳🥳🎉🎉🎉