引用这个操作在C语言中就已经存在了,它表示了一个取地址符,取地址符经常被用来在定义指针p的时候,在变量前面加上这个取地址符号,表示指针p指向了该变量的地址:
int* p = NULL;
int a = 0;
p = &a;
又或者是在函数的参数传递时候,形参定义的是指针,实参数变量的取地址,通过指针传递的形式来进行数据的交换操作:
void Test_PointerTransport(int* p, int* q){
int temp;
temp = *p;
*p = *q;
*q = temp;
}
int main(){
int serven_1 = 1;
int serven_2 = 2;
Test_PointerTransport(&serven_1, &serven_2); // 作用是交换连个数值
}
但是在C++中,引用还有另外一个用途,它不仅仅只有上面的取地址运算符一种功能。下面小编就来剖析一下C++中引用的相关知识点,当你掌握了这些知识点,在以后的开发过程中,你会发现你的代码的执行效率和代码质量完全提升了一个档次。
什么是引用
在C++中,引用就是将某个变量另取一个别名,像小时候我们村里面的老人都会给小孩子领取一个土名字一样(像小编的名字叫 “谢文”,有人给我起了一个“seven”),所以你叫我真名也行,叫我别名也是可以的,至少你在后面叫我,我还是会回头看你的~~~。
好了,引用就是给变量起一个别名的意思,对引用的操作和对变量直接操作的效果是完全一样的。引用的声明方法是:
类型标识符& 引用名 = 变量名;
代码:
int serven_1;
int& serven_2 = serven_1; // 给serven_1起一个别名叫做serven_2
在这里的符号&不是取址运算符,而是标识符,起到了标识的作用。
值得注意的一点是:
-
在声明引用时,一定要对其进行初始化;
-
声明了一个引用后,该引用不能再去重新引用别人了(这一点跟我们之前讲的指针常量一样,其实引用底层实现是指针常量);
-
声明引用只是对变量进行取别名的操作,不是定义一个新的变量来引用,所以serven_2本身就不是一种数据类型,系统也不会给引用分配存储单元,所以对引用求地址,就是直接对目标变量求地址(&serven_2 == &serven_1);
-
不能给数组建立引用,因为数组里面有很多个元素,都不知道你要引用哪个。
引用的原理
你在代码中看到的引用语句是这样的:
int serven_1;
int& serven_2 = serven1;
但是实际上,你的这两行代码等价于:
int serven_1;
int* const serven_2 = &serven_1; // serven_2是一个指针常量,这个指针常量指向变量serven_2
所以从上面的代码我们可以看到,引用的原理就是指针常量(在小编之前的一篇文章中有讲到指针常量、常量指针和常量指针常量之间的区别,你可以去看一看),一旦serven_2引用了serven_1这个变量,那么serven_2就不能在引用别的变量了,跟指针常量的做法是一样的,而且也允许通过引用去修改被引用目标变量的数值。
引用的几种引用场景
1. 引用作为函数参数
有开发经验的小伙伴都知道函数的参数传递有三种,分别是:值传递、指针传递和引用传递;
引用传递的代码如下:
void Test_Swap(int& serven_2){
serven_2 = 2;
}
void main(){
int serven_1 = 1;
Test_Swap(serven_1);
cout<<serven_1<<endl;
}
2. 常引用
常引用就是说该引用是一个常量,不能通过引用符号去修改被引用的变量的数值,常引用的声明方式:
const 类型标识符& 引用名=变量名;
代码:
int serven_1 = 1;
const int& serven_2 = &serven_1; // 一定要赋初始值
//serven_2 = 2; // 不允许这个操作,因为此时的引用serven_2已经是常量了
常量引用相当于常量指针常量,常量指针常量是指:该指针本身就是一个常量,不能够修改指向别的变量或者常量,并且不能通过指针的形式去修改变量的数值。
常引用的优点就是:安全性
因为当你的形参设定为常引用的话,你在函数里面不可以通过引用对变量进行修改,这也有助于代码的安全性。
3. 引用作为返回值
引用作为返回值是指你要调用的函数是一个引用函数。
类型标识符& 函数名(形参){}
int& Test_Area_Quote(){
int serven_1 = 1; // 局部变量,出了这个函数就会被销毁掉
return serven_1;
}
int& Test_Static_Quote(){
static int serven_2 = 2; // 静态变量,出了这个函数变量还在,只是出了这个函数就不能使能该变量了
return serven_2;
}
void Quote_Main(){
//int& serven_3 = Test_Area_Quote(); // 不允许返回局部变量做引用,因为局部变量出了作用域就会被销毁了
//cout<<"serven_3引用Test_Area_Quote函数后的返回引用值为:"<<serven_3<<endl<<endl;
int& serven_4 = Test_Static_Quote(); // 允许返回静态变量做引用,因为静态变量在程序的整个生命周期都存在
cout<<"serven_4引用Test_Static_Quote函数后的返回引用值为:"<<serven_4<<endl<<endl;
Test_Static_Quote() = 1000; // 因为此时serven_4引用了函数Test_Static_Quote,那么直接修改函数的返回引用值也是可行的
cout<<"e函数后的返回引用值为:"<<serven_4<<endl<<endl;
}
从代码可以看到,引用函数的返回值中,函数Test_Area_Quote返回的是局部变量,而在main函数中定义了一个引用来引用这个函数的返回值,这种操作是不允许的(偷偷告诉你在Windows操作系统中的VS是可以运行的,只是给了警告要求我们不要这么搞,但是在Linux操作系统中的gcc是编译不了的)。函数Test_Static_Quote返回的是静态变量,因为静态变量是在整个程序的运行期间都会存在的,所以可以进行引用的操作,并且,定义了serven_4去引用这个返回值,函数Test_Static_Quote()返回值的引用相当于有了个别名,这个别名是serven_4,所以可以对其进行改变数值赋值。
上面的代码运行结果:
4. 引用和多态
首先,我们得了解多态是什么?多态是指一个子类继承了父类,然后对父类的方法进行了重写的操作。
class serven;
class serven_1: public serven{...};
serven_1 Ser_1;
serven &Ser = Ser_1;
Ser是基类引用指向派生类,它只能来访问派生类对象中从基类继承的成员变量和成员函数。如果serven类中定义有虚函数,并且在serven_1类中重写了这个虚函数,就可以通过Ser产生多态效果。
最后,我们来看一下指针和引用的区别
指针 | 引用 |
初始化可以为空 | 初始化不能为空 |
可以指向别的变量 | 不能引用别的变量 |
sizeof得到的是指针本身的大小 | sizeof是所引用的变量(对象)的大小 |
指针++是地址++ | 引用++是数值++ |
动态分配内存的对象或者是分配内存一般使用指针,引用可能会引起内存泄漏。
关注公众号 “三贝勒文子” ,每天学习C++