【C++修炼之路】第四章---引用
1.引用的概念引用给已经存在的变量取别名编译器不会为引用变量开辟新的内存空间它和引用的变量共用一块空间。例如李华的小名叫作小华不管是小华还是李华都指的是同一个人。格式类型 引用变量名 被引用的实体。//例如 ra 为 a 的引用 int a 0; int ra a;看看两个变量的地址是否相同//比较二者的地址 cout a endl; cout ra endl;2.引用的性质1.引用变量的类型与引用实体的类型要相同int a 0; int ra a; char ch a; char rch ch;2.引用在定义时必须初始化int a 0; int ra a; //int ra; //错误的写法3.一个变量可以有多个引用int a 0; int b a; int c a; //...4.引用变量只能引用一个实体int a 0; int b 0; int ra a; ra b; //不能再引用其他变量 //对比指针 int* pa a; pa b;3.常量引用引用常量时记得用const修饰因为指针引用在赋值或初始化时权限可以缩小但不可以放大。//1. const int a 0; //int ra a; //编译时会报错a为常量//权限放大 const int ra a; //权限保持 //int b 10; //错误写法10为常量//权限放大 int c 0; const int rc c; //权限缩小 //2. int Count() { int a 0; return a 1; } int main() { //int ret Count(); //编译错误Count 返回值为常量 const int ret Count(); return 0; } //3. int a 0; //double b a; //类型错误 //double b (double)a; //编译错误类型转换时会产生临时变量 const double b (double)a; //证明产生的临时变量具有常量性质4.使用场景1.做参数//形参的改变会影响实参因为形参为实参的别名 void Swap(int a, int b) { int tmp a; a b; b tmp; } //对比指针 void Swap(int* a, int* b) { int tmp *a; *a *b; *b tmp; }//顺序表 typedef struct SeqList { int* a; int size; int capacity; }SL; void SLPushBack(SL* ps, int data);//C语言中 void SLpushBack(SL rs, int data);//C中//单链表 typedef struct Node { int data; struct Node* next; }Node,*PNode; void SLTPushBack(Node** pphead, int data);//C语言中 void SLTPushBack(Node* phead, int data);//C中 void SLTPushBack(PNode phead, int data);//C中2.返回值引用做返回值时如果函数返回时出了函数作用域如果返回对象还在还没还给系统则可以使用引用返回如果已经还给系统了则必须使用传值返回。int Count() { static int n 0;//出了作用域并未销毁可用引用返回 n; return n; }int Add(int a, int b) { int c a b;//c出了作用域会被销毁引用返回不可用 return c; } int main() { int ret Add(1, 2); Add(3, 4); cout Add(1, 2) is : ret endl;//看似结果为3实则不然 return 0; }引用作函数返回值时调用函数时可以修改返回对象#includeassert.h #define N 10 typedef struct Array { int a[N]; int size N; }Array; int Test(Array a, int i) { assert(i N); return a.a[i]; } int main() { Array a; for (int i 0; i N; i) { Test(a, i) i * 10; } for (int i 0; i N; i) { cout Test(a, i) ; } cout endl; return 0; }5.传值与传引用的效率比较以值作为参数或者返回值类型在传参或者返回期间函数不会直接传递实参或者将变量本身直接返回而是传递实参或者返回变量的一份临时的拷贝因此用值作为参数或者返回值类型效率是非常低下的尤其是当参数或者返回值类型非常大的时候效率就更低。//一组测试代码 #include time.h struct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 clock(); for (size_t i 0; i 10000; i) TestFunc1(a); size_t end1 clock(); // 以引用作为函数参数 size_t begin2 clock(); for (size_t i 0; i 10000; i) TestFunc2(a); size_t end2 clock(); // 分别计算两个函数运行结束后的时间 cout TestFunc1(A)-time: end1 - begin1 endl; cout TestFunc2(A)-time: end2 - begin2 endl; } int main() { TestRefAndValue(); return 0; }6.值和引用作为返回值的性能比较//一组测试代码 #include time.h struct A { int a[10000]; }; A a; // 值返回 A TestFunc1() { return a; } // 引用返回 A TestFunc2() { return a; } void TestReturnByRefOrValue() { // 以值作为函数的返回值类型 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; } int main() { TestReturnByRefOrValue(); return 0; }7.引用与指针引用与指针的用法及原理在我们使用者看来是不同的引用是给变量取别名指针是指向变量的地址但是其实引用的底层实现就是用指针的方式实现的。例如int main() { int a 10; int ra a; ra 20; int* pa a; *pa 20; return 0; }对比一下指针与引用的汇编代码发现二者完全相同。虽然底层实现方式相同但在使用时我们依然要注意二者的区别。指针与引用的不同点1.引用概念上是定义一个变量的别名指针是储存一个变量地址2.引用在定义时必须初始化指针没有要求3.引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体4.没有NULL引用但有NULL指针5.在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数32位平台下占4个字节6.引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小7.有多级指针但没有多级引用8.访问实体方式不同指针需要显示解引用引用编译器自己处理9.引用比指针使用起来相对更安全。