先给大家扯点别的,宾宾和卷天是计信学院的大一新生,有一天,卷天去宾宾宿舍找他玩,发现宾宾在学3D建模,卷天说:“靠北啦,你这个卷宾!”。
第二天,卷天将这件事告诉了龙龙,龙龙说:“你这个卷天真没礼貌,随便给宾宾起外号,宾宾再卷,能有你卷吗?”
在这个故事里,针对宾宾有两个称号,一个是他自己的名字,一个是卷天给他起的外号。一般来讲,不管你是喊宾宾还是卷宾,他都会回应你。
那这个故事跟引用有什么关系呢?没错,引用就是起个外号--------起别名。
一、引用的定义
1.1对单变量进行引用
语法:类型名 &外号 = 原名
示例:
int main()
{
int binbin = 10;
int &juanbin = binbin;//给宾宾取一个外号卷宾
cout << "我们先看两个变量的值是否一样" << endl;
cout << "binbin=" << binbin << endl;
cout << "juanbin=" << juanbin << endl;
cout << "我们再看一下他们的地址是否一样" << endl;
int* ptr_binbin = &binbin;//这里的&是取址
int* ptr_juanbin = &juanbin;
cout << "a=" << ptr_binbin << endl;
cout << "b=" << ptr_juanbin << endl;
}
从这里大家就可以看出,这两个变量不管是变量对应的值,还是地址,都是一样的。这说明,他们就是一个东西,这就是宾宾和卷宾之间的关系。
如果你任保持怀疑,那我们再给juanbin赋值看看。
int main()
{
int binbin = 10;
int &juanbin = binbin;//给宾宾取一个外号卷宾
cout << "修改前" << endl;
cout << "binbin=" << binbin << endl;
cout << "juanbin=" << juanbin << endl;
juanbin = 100;
cout << "修改后" << endl;
cout << "binbin=" << binbin << endl;
cout << "juanbin=" << juanbin << endl;
}
到此,我们可以说,引用,其实就是起外号。
但是要注意的是:
1.外号的类型名和被引用变量的类型名是一样的。下面这种情况就是错的
2.引用必须初始化,例如 int &b;这样就是错的。很好理解,你起一个外号”卷宾“,是给谁起的外号呢?宾宾啊,起外号,要给一个对象起,也就是说”卷宾“是给宾宾起的外号,你只说一个”卷宾“,别人是不知道这是宾宾的外号,同样的,你不初始化,电脑也不知道是谁的外号,所以引用必须要初始化。
3.引用一旦初始化后,就不可以更改了,例如
int c = 5;
int a = 10;
int &b = a;
int &b = c;
就是错的,b不能多次初始化
也很好理解,”卷宾“已经是宾宾的外号了,如果有一天卷天当着宾宾的面喊龙龙”卷宾“,宾宾肯定不能接受,因为宾宾是纯爱战士!!!!
那又有同学要说了,前面不是把juanbin从10改成100了吗?这只是赋值操作,不是更改引用。
赋值后还是宾宾的外号,不是牛头人。
1.2 对数组进行引用
上代码
int main()
{
int binbin[10];
for (int i = 0; i < sizeof(binbin) / sizeof(int); i++)
{
binbin[i] = i;//赋值
}
int(&juanbin)[10] = binbin;
cout << "使用外号访问数组元素" << endl;
for (int i = 0; i < sizeof(juanbin) / sizeof(int); i++)
{
cout << juanbin[i] << " ";
}
cout << endl;
//这边我们用指针访问数组
cout << "使用指针访问数组元素" << endl;
int* ptr_juanbin = juanbin;//此处是定义一个指针,并将其指向juanbin
//如果想获取地址,用*ptr_juanbin =& juanbin;
for (int i = 0; i < sizeof(juanbin) / sizeof(int); i++)
{
cout << *ptr_juanbin+ i << " ";
}
cout << endl;
}
这里只是想告诉你引用和指针十分相似,但是,引用很容易与指针混淆,它们之间有三个主要的不同:
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
1.3 引用作函数参数
关于值传递、地址传递和引用传递,我们用交换函数来举例子
1.值传递
void swap_zhi(int a, int b)
{
int t = a;
a = b;
b = t;
}
2.地址传递
void swap_dizhi(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
3.引用传递
void swap_yinyong(int &aa, int &bb)
{
int t = aa;
aa = bb;
bb = t;
}
现在我们用一串代码来将他们作比较
void swap_zhi(int a, int b)
{
int t = a;
a = b;
b = t;
cout << "值传递函数内部传递" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
void swap_dizhi(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
cout << "地址传递函数内部传递" << endl;
cout << "a=" << *a << endl;
cout << "b=" << *b << endl;
}
void swap_yinyong(int &aa, int &bb)
{
int t = aa;
aa = bb;
bb = t;
cout << "引用传递函数内部传递" << endl;
cout << "a=" << aa << endl;
cout << "b=" << bb << endl;
}
int main()
{
int a = 10;
int b = 100;
swap_zhi(a, b);
cout << "调用值传递函数结果" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
swap_dizhi(&a, &b);
cout << "调用地址传递函数结果" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
a = 10;//地址传递修改成了100 10,我们再把他改回来,便于观察
b = 100;
swap_yinyong(a, b);
cout << "调用引用传递函数结果" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
从上面的对比我们可以看出,值传递无法改变变量的值,只能能在函数内修改变量,而地址传递和引用传递可以修改变量的值
专业说法就是,值传递,形参不会修饰实参,而地址传递和引用传递可以。
1.4 引用作函数返回值
1.不能返回局部变量的引用
int& test()
{
int a = 10;//局部变量存放在四区的栈区,用完就释放
return a;
}
int main()
{
int& ref = test();
cout << "ref=" << ref << endl;
cout << "ref=" << ref << endl;
getchar();
return 0;
}
这里又有同学有疑问了,为什么第一次是对的,栈区不是用完释放吗,其实是因为编译器做了保留,但其实这一块的内容我们已经没有权利去操作他了,所以第二次是错的。
举个例子就很好理解了,因为某些原因,高数课宾宾和卷天没坐在一起,高数课结束了,卷天想找到宾宾,于是大喊“卷宾”,但是卷天怎么喊都找不到宾宾,因为宾宾下课后回宿舍学3D建模去了,高数课就是一个函数,而宾宾是函数的内部变量,函数结束就是高数课下课了,宾宾回宿舍就是局部变量被回收了。
如果我们执意想要返回引用的局部变量怎么办?
方法是将其变成静态变量。
int& test()
{
static int a = 10;//静态变量放在全局区,全局区上的数据程序结束后系统释放
return a;
}
int main()
{
int& ref = test();
cout << "ref=" << ref << endl;
cout << "ref=" << ref << endl;
getchar();
return 0;
}
也就是说卷天执意要找到宾宾,那就让宾宾下课后等他就行了。
但这样不推荐,看下面就明白了。
2.函数的调用可以作为左值
int& test()
{
static int a = 10;
return a;
}
int main()
{
int& ref = test();
cout << "ref=" << ref << endl;
cout << "ref=" << ref << endl;
test() = 1000;
cout << "ref=" << ref << endl;
cout << "ref=" << ref << endl;
getchar();
return 0;
}
妙不妙?
真是 妙蛙种子吃着妙脆角进了米奇妙妙屋 妙到家了!
test调用完之后返回的是a的引用,再对test进行操作其实就是对a的操作,而ref又是test的外号。
赋值操作后,再用别名来访问这块内存,其实这块内存已经被修饰掉了。
别忘了,引用其实就是起外号。
二、引用的本质
本质:引用的本质在c++内部实现是一个指针常量
void test(int& ref)
{
ref = 100;//ref是引用,转换为*ref = 100
}int main()
{
int a = 10;
//自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可以更改
int& ref = a;
ref = 20;//内部发现ref是引用,自动帮我们转换为:*ref = 20;
cout << "a=" << a << endl;
cout << "ref=" << ref << endl;
test(a);
getchar();
return 0;
}
三、引用的使用场景
3.1 指针的引用
3.2 常量的引用
能力有限,以后再写
本文参考b站视频黑马程序员,有兴趣可以去b站自行搜索,第一次写博客,组长的任务罢了。



