目录
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间
类型& 引用变量名(对象名) = 引用实体;
- int main()
- {
- int a = 1;
- int b = a;
-
- int& c = a;
- int& d = a;
- int& e = c;
-
- cout << &a << endl;
- cout << &c << endl;
- cout << &d << endl;
- cout << &e << endl;
- cout << &b << endl;
-
- return 0;
- }

引用类型必须和引用实体是同种类型的
1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
- int main()
- {
- int a = 0;
-
- // 引用必须初始化
- // int& b; 这是错误的
-
- int& c = a;
- int d = 1;
-
- // c变成d的别名?还是d赋值给c?--把d赋值给c
- c = d;
-
- // 一个对象可以有多个别名,可以别名继续取别名
- int& e = a;
- int& f = e;
-
- return 0;
- }

- void TestConstRef()
- {
- const int a = 10;
-
- //int& ra = a; // 该语句编译时会出错,a为常量 权限不能放大
- const int& ra = a;
-
- // int& b = 10; // 该语句编译时会出错,b为常量
- const int& b = 10;
-
- double d = 12.34;
- const double& rrd = d;//权限可以缩小
-
- //int& rd = d; // 该语句编译时会出错,类型不同
- const int& rd = d;//加了了const后 可以隐形类型提升
-
- int i = 1;
- double j = i;
- const double& rj = i;
- }
- // 传参数
- void Swap(int* left, int* right)
- {
- int temp = *left;
- *left = *right;
- *right = temp;
- }
-
- void Swap(int& left, int& right)
- {
- int temp = left;
- left = right;
- right = temp;
- }
-
- typedef struct SListNode
- {
- struct SListNode* next;
- int val;
- }SLTNode, * PSLTNode;
-
- //void SListPushBack(PSLTNode& phead, int x)
- void SListPushBack(SLTNode*& phead, int x)
- {
- if (phead == NULL)
- {
- // ...
- //phead = newnode;
- }
- else
- {
- // 找尾结点,再链接newnode
- // tail->next = newnode;
- }
- }
-
- int main()
- {
- int a = 0, b = 2;
- Swap(&a, &b);
- Swap(a, b);
-
- //PSLTNode plist = NULL;
- SLTNode* plist = NULL;
- SListPushBack(plist, 1);
- SListPushBack(plist, 2);
- SListPushBack(plist, 3);
-
-
- return 0;
- }
我们可以看到插入的时候我们就没有传地址了. 接收的就是她本身, 只是换取了一个名字
引用类型必须和引用实体是同种类型的
- // 出了函数作用域,返回对象就销毁了,不能用引用返回,否则结果是不确定
- int& Count()
- {
- int n = 0;
- n++;
-
- return n;
- }
-
- int main()
- {
- int ret = Count();
- cout << ret << endl;
-
- cout << ret << endl;
-
- return 0;
- }

- // 出了函数作用域,返回对象就销毁了,不能用引用返回,否则结果是不确定
- int& Add(int a, int b)
- {
- int c = a + b;
- return c;
- }
-
- int main()
- {
- int& ret = Add(1, 2);//这里ret本质也是c的别名
- cout << "Add(1, 2) is :" << ret << endl;
-
- Add(3, 4);
-
- cout << "Add(1, 2) is :" << ret << endl;
- return 0;
- }
-

- int& Add(int a, int b)
- {
- static int c = a + b;//静态变量 只接受一次初始值
- return c;
- }
-
-
- int main()
- {
- int& ret = Add(1, 2);
- cout << "Add(1, 2) is :" << ret << endl;
-
- Add(3, 4);
-
- cout << "Add(1, 2) is :" << ret << endl;
- return 0;
- }

- typedef struct SeqList
- {
- int a[100];
- int size;
- }SL;
-
- //void SLModify(SL* ps, int pos, int x)
- //{
- // //...
- // assert(ps);
- // assert(pos < ps->size);
- // ps->a[pos] = x;
- //}
-
- //引用做返回值:可以修改返回对象
- int& SLat(SL* ps, int pos)
- {
- assert(ps);
- assert(pos < ps->size);
-
- return ps->a[pos];
- }
-
- int main()
- {
- SL s;
- //...
-
- SLat(&s, 3) = 10;
-
- // 每个位置的值++
- for (size_t i = 0; i < s.size; i++)
- {
- SLat(&s, i)++;
- }
-
- return 0;
- }
这里SLat函数里的ps不是在此函数里定义的, 是在main函数定义传参过来的, 所以出了这个SLat函数, 返回值不会销毁, 所以可以引用返回.
怎样判断?
只要这个对象没有被定义在这个函数里, 即便函数执行结束了, 那么这个对象不会被销毁, 所以它的引用是存在的.
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直 接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
- #include
- 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();
- }

- #include
- 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();
- }

引用返回秒杀值返回
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作
- int main()
- {
- int a = 10;
-
- // b是否开空间? -- > 没有
- int& b = a;
-
- // ptr开了空间
- int* ptr = &a;
-
- char ch = 'x';
- char& r = ch;
- cout << sizeof(r) << endl;
- return 0;
- }
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做
引用和指针的不同点:
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求 引用一旦定义时初始化指定,就不能再修改,指针可以改变指向
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体 指针可以改变指向,引用不能
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全
本节讨论的主要就是引用, 引用的重要性很大, 可以说贯穿了整个C++过程, 只不过现在肯定感受不到的, 要在后面类和对象的学习中, 才能深刻感受到它的作用和魅力, 有些是指针不能替代的. 最后还是那句话, C基础不扎实的看这个会困难, 可以看我之前C语言的博客.