前言
本文将讲述,C++中的引用&
🕺作者: 迷茫的启明星
😘欢迎关注:👍点赞🙌收藏✍️留言
🏇家人们,码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!
持续更新中~
引用&
引用的概念
引用并不是定义一个新的变量,而是给已经存在的变量取一个别名,相当于外号,编译器不会给它开辟内存空间,它和它所引用的变量共用一块空间。
比如说:宋江,江湖人称“及时雨”,本质都是宋江,不过是一个外号
引用的格式:
类型 & 引用变量名=引用实体;
void test1 { int a=1; int& b=a;//定义引用的类型 cout< cout< } ``` 这里要注意的是,引用的类型必须和引用的实体是同种类型的。
引用的特性
引用在定义的时候必须初始化
也就是说它引用的是已经存在的变量(空间)
一个变量可以有多个引用
也就是说给一个实体取多个“外号”
引用一旦引用一个实体,就不能再引用其他的实体了
也就是说不能再把这个“外号”给别人了
void test2() { int a=1; int x=2; //int &b;//这个引用没有初始化,编译会报错 int &b=a; int &c=a;//多个引用(多个外号) //int &c=b//引用多个实体,不同实体有相同的外号,会报错 }
常引用
int main() { // 权限放大 不可以 const int a = 10; int& b = a; // 权限不变 可以 const int a = 10; const int& b = a; // 权限的缩小 可以 int c = 10; const int& d = c; return 0; }
在引用时要注意,只能把自己有的给别人,自己都没有权限怎么给呢?
引用的应用
在之前我们实现链表是使用指针来做参数的,现在我们就可以利用引用的性质来实现它。
譬如:实现尾插
之前使用指针是这样的(仅展现部分代码,理解就好)
void SListPushBack(SLTNode** pphead, SLTDateType x) { SLTNode* newnode = BuyListNode(x); if (*pphead == NULL) { *pphead = newnode; } else { // 找到尾节点 SLTNode* tail = *pphead; while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; } } int main() { SLTNode* plist = NULL; SListPushBack(&plist, 1); SListPushBack(&plist, 2); SListPushBack(&plist, 3); SListPushBack(&plist, 4); return 0; }
现在我们就可以这样写
void SListPushBack(SLTNode*& pphead, SLTDateType x) { SLTNode* newnode = BuyListNode(x); if (pphead == NULL) { pphead = newnode; } else { // 找到尾节点 SLTNode* tail = pphead; while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; } } int main() { SLTNode* plist = NULL; SListPushBack(plist, 1); SListPushBack(plist, 2); SListPushBack(plist, 3); SListPushBack(plist, 4); return 0; }
pphead是plist的别名,改变pphead就是改变plist
做参数
使用引用的好处就在于,没有指针那么复杂,还要使用二级指针,难以理解。再就是,定义函数时,要么就是传值,要么就是传址,现在就有了第三种情况传引用。
void swap(int r1, int r2) // 传值 { int tmp = r1; r1 = r2; r2 = tmp; }
void swap(int* p1, int* p2) // 传地址 { int tmp = *p1; *p1 = *p2; *p2 = tmp; } void swap(int& r1, int& r2) // 传引用 { int tmp = r1; r1 = r2; r2 = tmp; }
引用又有了另一个好处
它不需要开辟新的空间,是在原空间操作,这在数量级小的时候不起眼,可是在递归时就显得尤为可贵了,虽说传地址所需空间也少,但是在有时候,还是不如&方便。
#include <time.h> struct A{ int a[10000]; }; void TestFunc1(A a){} void TestFunc2(A& a){} int main() { A a; // 以值作为函数参数,每次都要拷贝40000byte size_t begin1 = clock(); for (size_t i = 0; i < 1000000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数,无需拷贝 size_t begin2 = clock(); for (size_t i = 0; i < 1000000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; return 0; }
这里还要注意一个点,如果使用引用传参,函数中不改变参数的值,建议使用const &
做返回值
#include <time.h> struct A{ int a[10000]; }; A a; // 值返回 -- 每次拷贝40000byte A TestFunc1() { return a; } // 引用返回 -- 没有拷贝 A& TestFunc2(){ return a; } int main() { // 以值作为函数的返回值类型 size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(); size_t end1 = clock(); // 以引用作为函数的返回值类型 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(); size_t end2 = clock(); // 计算两个函数运算完成之后的时间 cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; return 0; }
使用引用作返回值时,看上去是不是觉得效率提升了很多?但是这里有个大坑,虽说传值返回会生成拷贝,但是它在结束时会产生一个临时变量,临时变量再给调用的地方,至少是安全的
这里提一下临时变量存在哪里
如果比较小(4/8),一般是存在寄存器中
如果比较大,就会放在调用函数的栈帧中
接前面继续讲
引用做返回值,它引用的是什么呢?
是返回值,但是我们要知道,函数结束后,栈帧就销毁了,
它这个时候就相当于非法访问了,它访问的就可能是随机值(编译器不同而不同),
那么我们应该怎么使用它呢?
需要记住这个规则:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
比如:
int& count() { static int n;//静态 n++; retuern n; }
它就可以使用引用作返回值
引用和指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
int main() { int a = 10; int& ra = a; cout<<"&a = "<<&a<<endl; cout<<"&ra = "<<&ra<<endl; return 0; }
但是,在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20; return 0; }
我们来看下引用和指针的汇编代码对比
引用和指针的不同点:
引用概念上定义一个变量的别名,指针存储一个变量地址。
引用在定义时必须初始化,指针不是必须
引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
没有NULL引用,但有NULL指针
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
有多级指针,但是没有多级引用
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
引用比指针使用起来相对更安全,因为不用像指针那样要考虑野指针、空指针等问题。
总结
本文讲述了,C++中的引用&,下一篇将讲述C++初识下篇。
respect !
下篇见!