目录 Table of Contents
7 引用类型
引用类型是 C++ 拓展出来的一个新的类型
1_引用就是变量的 "别名"
引用类型就是给变量起个别名, 起个另外的名字. 你操作变量的别名, 就相当于操作变量本身.
引用类型是必须要赋初始值的, 不能先 int& ref;
然后再 ref = x;
, 编译器报错 : references must be initialized
-
基本类型
int x = 1; int& p = x; //起个别名叫 p p = 2; // p 就是 x printf("%d\n",x);
-
类
Person p; Person& px = p; // 起个别名叫作 px px.x = 10; // px 就是 p printf("%d\n",p.x);
-
指针
int******** i = (int********)1; int********& r = i; // 起个别名叫 r r = (int********)2; // r 就是 i printf("%d\n",r);
-
数组
int arr[] = {1, 2, 3}; int (&p)[3] = arr; // 现在 p 是数组 arr 的别名 p[0] = 4; printf("%d\n",arr[0]);
2_引用类型的本质
int main(int argc, char* argv[])
{
int x = 10;
int& ref = x;
printf("%d\n",x);
return 0;
}
ref 存储的就是变量 x 的地址
int main(int argc, char*argv[])
{
int x = 10;
int* ref = &x;
printf("%d\n",x);
return 0;
}
我们把它改成指针, 观察反汇编发现反汇编代码和刚才一模一样.
我们现在看一下类类型
Base b;
b.x = 1;
Base& ref = b;
ref.x = 2;
printf("%d\n",b.x);
现在得出结论, 引用生成的汇编代码, 和指针的汇编代码一模一样
我们看看引用与指针的区别
3_引用类型与指针的区别
int x = 1;
// 必须初始化
int* p = &x;
int& ref = x;
// 运算
p++;
ref++;
// 赋值
p = (int*)1;
ref = 100;
当我们对指针做运算的时候, 实际上是对指针本身做运算; 实际上, 你对引用做运算, 引用本身说明不能任何问题.
p++
, p 这个指针本身加了 1,int 是 4 个字节, 所以这个指针指向的地址往后面移动了 4 个字节
ref++
, 这只是一个别名, x++
p = (int*)1;
这么赋值之后, p 的值就变成 1 了, 就这么简单
那么 ref 重新赋值 ref = 100;
, ref 本身存的是 x 的地址, 这个永远不变, ref = 100;
改的是 x 的值
现在我们通过类类型进一步了解引用的特点
class Base
{
public:
int x;
int y;
Base(int x, int y)
{
this->x = x;
this->y = y;
}
};
int main(int argc, char* argv[])
{
Base b(1,2);
// 初始化
Base* p = &b;
Base& ref = b;
// 运算
p++;
//ref++; 编译不过去
return 0;
}
初始化的时候二者汇编代码没啥区别
运算的时候, ref++
编译不过去了, 你直接在这写 ref++
相当于就是在这里写 b++
, b 是一个对象, 它这个 ++ 不能直接用啊, 所以编译不过去.
现在来看, 引用类型, 除了赋值的时候它看着像指针, 其它时候, 它代表的就是那个变量.
你代表的是 a, 那你就能做 a 所可以做的事情, a 能 ++, 你这个引用就可以 ++
总结
-
引用必须赋初始值, 且只能指向一个变量, "从一而终"
-
对引用赋值, 是对其指向的变量赋值, 而并不是修改引用本身的值
-
对引用做运算, 就是对其代表的变量做运算, 而不是对引用本身做运算
-
引用类型就是一个 "弱化了的指针"
为什么会出现引用这么个东西呢, 因为如果你驾驭不住指针的话, 程序就容易出错
4_引用在函数参数传递中的作用 (基本类型)
void Plus(int& i)
{
i++;
return;
}
int main(int argc, char* argv[])
{
int i = 10;
Plus(i);
printf("%d\n",i);
return 0;
}
我们这里参数是引用类型, 按照我们以前基本类型的方式, 这里传的应该是 10 这个值进去, 但这里是引用类型, 我们来看一下它到底干了什么
观察反汇编代码, 发现这里穿进去的是变量的地址, 传递的时候像指针. 然后 i++;
操作, 这个操作并不是对指针本身进行操作, 而是对它所代表的那个变量进行 ++ 操作, 就是把这个变量地址取出来, 然后把地址存的那个值 10 取出来加 1 然后存回去.
5_引用在函数参数传递中的作用 (构造类型)
struct Base
{
int x;
int y;
Base(int x, int y)
{
this->x = x;
this->y = y;
}
}
void PrintByRef(Base& refb, Base* pb)
{
// 通过指针读取
printf("%d %d\n",refb.x, refb.y;
// 通过引用读取
printf("%d %d\n", refb.x, refb.y);
// 指针可以重新赋值, 可以做运算
// refb = (Base&)1;
// refb++;
}
int main(int argc, char* argv[])
{
Base b(1,2);
PrintByRef(b, &b);
return 0;
}
参数传递, 都是取地址, 看不出任何差异
通过指针读取, 很好理解
通过引用读取, 没有任何区别
所以使用的时候, 我们没有看到任何差异
6给狗起个人的名字 ?
引用是变量的别名, 比如 :
int x = 10;
int& r = x; // int 类型的别名就应该是 int&
Base b(1, 2);
Base& r = b; // Base 类型的别名就应该是 Base&
Base& r = (Base&)x; // 虽然可以编译, 但是意义不大
给狗起个人的名字 ? 就是说我使用引用的时候, 我不按照它的规则去给它起个别名, 允许还是不允许 ?
比如上面代码, 我们给 int 类型起一个 Base 类型的别名. 编译器允许, 但是没啥意义.
我们在使用引用的时候, 人家原来是什么类型, 你就起一个什么类型的别名.
7_常引用
class Base
{
public:
int x;
};
void Print(const Base& ref) // 常引用
{
// ref = 100; // 不能修改
// ref.x = 200; // 但能修改里面的内容
printf("%d\n", ref.x);
}
int main(int argc, char* argv[])
{
Base b;
b.x = 100;
Print(b);
return 0;
}
如果对象很大的话, 我们不能直接传对象进去, 虽然对象可以作参数来传递, 但是内存效率太低, 不推荐.
常引用就是当我们希望对某一个对象, 进行只读操作的时候, 通过 const 关键词加上引用来达到我的目的.