[C++] 引用详解
引用的基础用法和本质
引用和consts
引用和返回
引用的基础用法和本质
/******************************************************************* a) 在C++中新增加了引用的概念 b) 引用可以看作一个 已定义[变量] 的【别名】 c) 引用的语法:Type& name = var; d) 普通引用在声明时必须用其它的变量进行初始化, 引用作为函数参数声明时不进行初始化 ******************************************************************/ #include <iostream> #include <cstdio> using namespace std; /*---------------------------------------------------------------------*/ struct Teacher { char name[64]; int age; }; Teacher t1; void printfT(Teacher *pT) { cout<< "printfT_pT->age:" << pT->age <<endl; } //pT是t1的别名 ,相当于修改了t1 void printfT2(Teacher &pT) { //cout<<pT.age<<endl; pT.age = 33; } //pT和t1的是两个不同的变量 void printfT3(Teacher pT) { pT.age = 45; //只会修改pT变量 ,不会修改t1变量 cout<< "printfT3_pT.age:" << pT.age <<endl; cout<< "printfT3_t1.age:" << t1.age <<endl; } /******************************************************** * 引用 最基本的一个功能就是 别名
* 引用是内存空间的别名,字面量19没有内存空间 ********************************************************/ int main01(void) { int a = 10;//c编译器分配4个字节内存 a是内存空间的别名 int &b = a; //b就是a的别名 a = 11; //直接赋值 printf("a:%d\r\n", a);//11 int *p = &a; *p = 12; printf("a:%d\r\n", a);//12 b = 14; printf("a:%d b:%d", a, b);//14 14 return 0; } /******************************************************** * printfT(Teacher *pT) 结构体指针 * printfT2(Teacher &pT) 引用 * printfT3(Teacher pT) 形参 ********************************************************/ int main02(void) { //05复杂数据类型 的引用 t1.age = 35; printfT(&t1);//取t1的地址 printfT2(t1); //pT是t1的别名 printf("printfT2_t1.age:%d \n", t1.age); //33 /*函数调用,实参传递给形参时,不是用实参替换(取代)形参,而是用实参给形参赋值*/ /*我在github上写了一份代码,你可以clone一份给自己,但是不会影响我的代码*/ printfT3(t1);// pT是形参 ,t1 copy一份数据 给pT //---> pT = t1 printf("t1.age:%d \n", t1.age); //33 cout<< "hello..." <<endl; return 0; } /******************************************************** * 引用在C++中的内部实现是一个【常指针】 * * Type& name <==> Type* const name * ********************************************************/ int main03(void) {//引用的本质 int a = 10; int &b = a;//单独定义的引用时,必须初始化; int &c = b; b = 11; cout << "c-->" << c << endl; printf("a:%d\r\n", a); printf("b:%d\r\n", b); printf("&a:%d\r\n", &a);//2424372 printf("&b:%d\r\n", &b);//2424372 printf("sizeof(b) %d", sizeof(b));//4 引用有地址,引用是一个常量 return 0; /* 引用在C++中的内部实现是一个【常指针】 Type& name <==> Type* const name C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。 从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏 */ } /*------------------------------承接上面------------------------------*/ void func(int &a) { a = 5; } void funx(int* const a) { //常指针a *a = 7; } int main(void) { int x = 10; func(x); cout << "func(10):" << x << endl; funx(&x); cout << "funx(10):" << x << endl; return 0; } /*======================== ====================== END File ========================== =====================*/
引用和const
引用是引用,const是const
/*_________________________________________________________________________ 1)普通引用 int& e1 相当于 int* const e1 常(量)指针 2)常引用 const int& e1 相当于 const int* const e1 在C++中可以用const来声明引用 【常引用 <=> const引用】 -> const引用让变量e1本身和其所指向的内存空间均只拥有只读属性
const引用分 变量的常引用 和 常量的常引用 两种 A.变量的常引用
const Type& name = var; 存在内存泄露 B.常量的常引用
引用是内存空间的别名 字面量19没有内存空间 当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名 使用字面量对const引用初始化后,将生成一个只读变量 __________________________________________________________________________*/ #include <iostream> using namespace std; int main(void) { /*================================= 使用变量初始化const引用 ===============================*/ int k1 = 0; const int& rk = k1;//变量的常引用 const int* const rk = k1; // rk = 1;//err 不可以通过引用rk修改k1 k1 = 1;//ok cout << " k1:" << k1 << "\n rk:" << rk << "\n &k1:" << &k1<< "\n &rk:" << &rk << endl; //可以通过指针来修改rk,然后就修改了k1 int *p = (int* )&rk; //取rk的地址赋值给p 其实这里存在一个内存泄露,因为rk本身和rk的内存空间均是不可修改的,而这里修改了 *p = 11; cout << "\n k1:" << k1 << "\n rk:" << rk << "\n &k1:" << &k1 << "\n &rk:" << &rk << endl; /*=========================== 使用字面量常量初始化const引用 ===============================*/ const int b = 10;//只是一个常量b // b = 11; //err cout << "\n &b:" << &b << endl; // int &a = 19;//err 引用是内存空间的别名 字面量19没有内存空间 const int &a = 19;//ok 这里是引用a cout << "\n a:" << a << endl; cout << "\n &a:" << &a << endl; return 0; }
引用和返回
/*________________________________________________________________________ C++引用使用时的难点: 当函数返回值为引用时 若返回栈变量 不能成为其它引用的初始值 不能作为左值使用 ===>不能返回局部变量的引用 若返回静态变量或全局变量 可以成为其他引用的初始值 既可作为右值使用,也可作为左值使用 C++链式编程中,经常用到引用,运算符重载专题 ____________________________________________________________________________*/ #include <iostream> #include <cstring> //memset #include "stdlib.h"//malloc #include <cstdio> using namespace std; /*********************************************** 对比同一种类型的两种即可看出引用的优势 ***********************************************/ /*------ 局部参量返回作引用 -------*/ /* error int& getAA2() { int a = 10; return a; } */ /*********************************************************************************** * 全局参量返回作引用 * 以返回值的形式返回, 系统生成要返回值的副本(即临时变量) * 以引用的形式返回, 系统不生成返回值的副本 , ***********************************************************************************/ float temp; //定义全局变量temp float fn1(float r); //声明函数fn1 float& fn2(float r); //声明函数fn2 float fn1(float r) {//定义函数fn1,它以返回值的方法返回函数值 temp = (float)(r*r*3.14); return temp; } float& fn2(float r) {//定义函数fn2,它以引用方式返回函数值 temp = (float)(r*r*3.14); return temp; } /******************************************************** * static变量返回作引用 * 好处和全局变量一样,不用返回副本 ********************************************************/ int getBB1() { static int a = 10; a++; printf("getBB1.a:%d\n", a); return a; } int& getBB2() { static int a = 10; a++; printf("getBB2.a:%d\n", a); return a; } int* getBB3() { static int a = 10; a++; printf("getBB3.a:%d\n", a); return &a; } /******************************************************** * 形参返回作引用 ********************************************************/ int g1(int *p) { *p = 100; return *p; } int& g2(int *p) { *p = 100; return *p; } /*------ 指针返回作引用 -------*/ struct Teacher { char name[64]; int age; }; int getTe(Teacher* *myp) { Teacher* p = (Teacher* )malloc(sizeof(Teacher));//dynamic memory if (p ==NULL) return -1; memset(p, 0, sizeof(Teacher)); p->age = 33; *myp = p;// return 0; } int getTe2(Teacher* &myp) { myp = (Teacher *)malloc(sizeof(Teacher)); myp->age = 44; return 0; } int main() { //主函数 /*=================== 局部参量返回作引用 ==============================*/ // int a2 = getAA2(); /********因为不能从被调函数中返回一个临时变量或局部变量的引用*********/ /*==================== 全局参量返回作引用 ==============================*/ float a = fn1(10.0); //第1种情况,系统生成要返回值的副本(即临时变量) // float &b = fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定) /*因为不能从被调函数中返回一个临时变量或局部变量的引用*/ //用引用去接受函数的返回值,是不是乱码,关键是看返回的内存空间是不是被编译器回收了 float c = fn2(10.0); //第3种情况,系统不生成返回值的副本 /* 可以从被调函数中返回一个全局变量的引用 */ float &d = fn2(10.0);//第4种情况,系统不生成返回值的副本 /* 可以从被调函数中返回一个全局变量的引用 */ cout<< "a: " << a <<" c: " <<c <<" d: " <<d <<endl; /*====================== static变量返回作引用 =============================*/ int b1 = getBB1(); int b2 = getBB2(); int *b3 = getBB3(); printf("b1:%d b2:%d b3:%d\n", b1, b2, *b3); /*====================== 形参返回作引用 =============================*/ int cc = 10; int c0 = g1(&cc); int c1 = g2(&cc); int& c2 = g2(&cc); //用引用去接受函数的返回值,是不是乱码,关键是看返回的内存空间是不是被编译器回收了 printf("c0:%d ", c0); printf("c1:%d ", c1); printf("c2:%d \n", c2); /*====================== 类返回作引用 =============================*/ //涉及到copy构造函数 和 =操作重载 /*================================= 指针引用 ===============================*/ Teacher *p = NULL; // getTe(&p);//C语言中,修改p的值,只能发送p的地址 getTe2(p);//C++中有了引用,可以直接传递p printf("age:%d \n", p->age); //因为是在子函数getTe内部开辟了一个动态内存,所以可以跨函数使用内存 /* 关于 C指针 的几点回顾 1. fun(p1, p2); void fun(char *a, char *b); 传的是谁,谁就是实参(指针p1, p2)! 形参(指针 a,b)的变化是不会影响实参(指针p1, p2)的变化的! 2.getTe(&p); C语言中,修改p的值,只能发送p的地址,且如果跨函数使用内存,必须开辟动态内存 */ return 0; }