目录
1、引用做参数 (输出型参数)形参的改变要影响实参2、引用做参数 (提高效率) (大对象/深拷贝对象--以后补充)
2、引用做返回值(不会生成临时变量)(慎用,当值为局部变量时,会很危险,出作用域后,栈帧就被销毁,值被覆盖)
引用
给名字起个外号,别名(语法层面看没有开辟空间,底层汇编指令的角度看引用是类似指针的方式实现的)
引用必须得给一个值(初始化)
int&d错误
int&d=a;正确
一个变量可以有多个引用,
一个引用一旦引用一个实体,再也不能引用其它实体以后无法改变 (C++跟java的差异
引用的应用场景
1、引用做参数 (输出型参数)形参的改变要影响实参
2、引用做参数 (提高效率) (大对象/深拷贝对象--以后补充)
//在C语言中,想要改变实参,需要传实参的地址,现在我们只需要在实参类型后面加一个&(引用)
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) -- c
int* preorderTraversal(struct TreeNode* root, int& returnSize) -- cpp
void Swap(int*& a, int*& b)
{
int* tmp = a;
a = b;
b = tmp;
}
typedef struct ListNode
{
int val;
struct ListNode* next;//c,cpp(兼容c)
ListNode* next;//cpp
}LTNode;
typedef struct ListNode
{
int val;
struct ListNode* next;
}LTNode, *PLTNode;
//void ListPushBack(struct ListNode*& phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PLTNode& phead, int x)
{
// ...
// phead = newnode;
// ...
}
int main()
{
int x = 0, y = 1;
//不需要传递实参地址,只需要将实参值传过去
Swap(x, y);
cout << x << " " << y << endl;
int* px = &x, * py = &y;
cout << px << " " << py << endl;
Swap(px, py);
cout << px << " " << py << endl;
PLTNode plist = NULL;
ListPushBack(plist, 1);
return 0;
}
我们可以通过一组代码来看普通传参与引用传参的速度对比
#include<iostream>
#include <time.h>
using namespace std;
struct A { int a[100000]; };
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;
}
2、引用做返回值(不会生成临时变量)(慎用,当值为局部变量时,会很危险,出作用域后,栈帧就被销毁,值被覆盖)
作用:减少拷贝,提高效率;
#include<iostream>
using namespace std;
#include<assert.h>
#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;
}
修改返回值+获取返回值;
传值返回
传值返回,不管怎么样,都会生成临时变量
函数返回值不会直接返回,在寄存器中,生成临时变量
寄存器只有4字节或者八字节
int Count()
{
static int n=0;(静态变量,也会生成临时变量)
n++;
return n;
}
传引用返回
减少拷贝,提高效率
n为静态变量时不危险,但是n为局部变量时,会发生非法访问,结果可能为随机值
正确样例:
全局变量。静态变量,malloc
int& Count()
{
static int n=0;
n++;
return n;(返回的是n的别名,没有拷贝)
}
错误样例
顺序表,位置读写与修改,
int& SLAt(SeqList&ps,int pos)
{
return ps.a[pos];
}
SLAt(s,0)=1;该位置赋值为1
SLAt(s,0)+=5;该位置值加5
引用总结
1、基本任何场景都可以用引用传参。
2、谨慎使用引用做返回值,出来函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回。
命名空间域不会影响生命周期。
常引用
引用时,才会出现权限放大、平移、缩小
//不可以
引用过程当中,权限不能放大
const int a=0;
int& b=a;错误
//可以,c拷贝给d,没有放大权限,因为d的改变不影响c
const int c=0;
int d=c;
//可以
引用过程中,权限可以平移或缩小
int x=0;
int &y=x;//平移
const int&z=x;//缩小
++x;正确
--z;错误
可以
const int&m=10;//平移
不可以
double dd=1.11;
int ii=dd; 不同类型转换会产生临时变量,提升,临时变量具有常性
int main()
{
int i=1;
double j=1.1;
if(j>i)//在运算符两边的两个变量类型不同时会发生提升(i会提升,但不会对i本身进行提升,因为在这不能把i给修改了),因此生成一个double的临时变量,8字节。一般情况下都是小的向大的提升
{
cout<<"xxxxx"<<endl;
}
return 0;
}
int&rii=dd;错误 拷贝的是int临时变量;临时变量具有常性
const int&rii=dd;正确
引用和指针的区别(面试常考点)
int main()
{
//语法层面看没有开辟空间,底层汇编指令的角度看引用是类似指针的方式实现的)
int a = 10;
// 语法层面:不开空间,是对a取别名
int& ra = a;
ra = 20;
// 语法层面:开空间,存储a的地址
int* pa = &a;
*pa = 30;
return 0;
}
因为内存太慢,所以需要借助寄存器
lea(取地址) 将a的地址放到eax寄存器中
[ ]表示解引用 把eax中a的地址放到pa中
取出pa里面的内容,a的地址放到eax中去
对eax进行解引用,获取a地址里面的内容,将其修改为30
auto关键字
一般类型过长时,用auto替代(例如迭代器)
int a=0;
int b=a;
auto c=a;//根据右边的表达式推导c的类型
auto d=1+1.11;
cout<<typeid(d).name()<<endl;(打印类型)
注意事项:
auto xx;错误
auto c=3,d=4.0;错误
auto不能做函数参数,不能直接用来声明数组
范围for 语法糖
适用于数组,容器
依次取数组中数据赋值给e(element元素)
自动迭代(往后走),自动判断结束
auto与e都是可修改的,但是建议用这两个
#include<iostream>
全部展开
//using namespace std;
//部分展开
using std::cout;
using std::endl;
int main
{
int arr[] = { 2,4,6,8,10 };
//赋值操作(这样无法修改数组中的值,因为这是一种赋值操作,需要加一个引用)
for (auto e : arr)
{
e *= 2;
}
for (auto e : arr)
{
cout << e << " ";
}
cout << endl;
//引用操作
for (auto& e : arr)
{
e *= 2;
}
for (auto e : arr)
{
cout << e << " ";
}
cout << endl;
return 0;
}
错误用法
//在这里,数组传参变成了指针
void TestFor(int array[])
{
for(auto& e : array)
cout<< e <<endl;
}