文章概述
- 引用的定义以及格式
- 引用的初始化
- 引用的本质
- 函数的返回值是引用
- 指针的引用以及常量引用
引用的定义以及格式
a. 引用的定义: 对已定义的变量起的别名。定义引用的时候,必须绑定已定义好的变量。
b. 引用的格式: Type & name =var(已经定义好的变量);
int a =10;
int&b =a;
普通引用必须和绑定的对象的类型相同,(上面的就是普通引用)。操作引用和操作绑定的对象是一致的。
引用的初始化
引用可以分为普通引用和特殊引用。
a. 对于普通引用,定义的时候必须赋值,也就是初始化。
b. 作为函数的形式参数时,可以不进行初始化。
//普通引用
int a =10;
int&b =a;
//特殊引用(作为函数的形式参数)
void func(int& a)
{
...
}
引用的本质
引用的本质就是在C++内部的实现。首先分析下面对象的所占的内存空间:
struct StudentOne
{
double a;
int b;
}
StudentOne对象所占的内存空间是8+4=12(对于结构体字节对齐的问题已经考虑到)
struct StudentTwo
{
double a;
int b;
int&c;
}
StudentTwo对象所占的内存空间是8+4+4=16(对于结构体字节对齐的问题已经考虑到)
我们知道引用是有自己的内存空间的。从我们使用角度,我们误以为引用是一个别名没有自己的内存空间,这是C++为了实用而作出的细节屏蔽。现在我们知道引用是有内存空间类似于指针。
int a =10;
int&b =a;
我们知道上面的代码是定义引用,引用的定义是不是和定义常量类似(const int a)。根据上面的两点,我们可以得出引用在C++的内部实现就是常量指针。
引用本质的结论: (1). 引用在C++中的内部实现是一个常量指针; (2). C++编译器在编译的过程中使用常量指针作为引用的内部实现,因此引用占用的内存空间与指针相同;(3). 从使用的角度来看,引用会让人误会其实是一种别名,没有自己的存储空间,这是C++为了实用性而做出的细节屏蔽。
下面的代码分析:
void func(int& a)
{
a=5;
}
int main()
{
int p =10;
func(p);
}
调用函数 func(p)的时候,C++编译器会自动的取p得地址赋给a,不用我们手动的去地址。上面的函数func(int& a)内部实现就是下面:
void func(int * const a)
{
*a=5;
}
我们知道调用这个函数时,C++编译器会自动将地址赋给a,不需要手动的取地址就OK了。
这里提下间接赋值,间接赋值的条件:
a. 定义两个变量(a,b)
b. 建立关联(将a的地址赋给b)
c. b可以间接的改变a的值
引用的应用场景: 其实把间接赋值成立的三个条件中的第二个和第三个写到一起,第一个单独在一起。
函数的返回值是引用
(1). 我们首先看返回值是普通变量:
int getA()
{
int a=10;
return a;
}
函数调用时,进入getA()的内部。C++编译器会给局部变量a分配一个内存空间。return a表示函数结束并且返回一个值给函数调用者。函数调用结束后,C++编译器将内存空间a的内容消除。
(2). 函数的返回值是引用:
int& getB()
{
int a=10;
return a;
}
函数调用时,进入函数内部。C++编译器会分配内存给a,注意: 返回的是变量a的地址以及a的值。(上面我们已经说过)
我们已经得知函数返回值是普通变量时,调用者只获得局部变量的值;函数返回值是引用时,调用者会获得局部变量的地址。
分析下面的代码a,b是否能够正常输出:
int& getB()
{
int a=10;
return a;
}
int main()
{
int a =getB();
int &b=getB();
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
输出结果:
为什么会出现这个结果? 对于变量a来说, 只是获得函数返回值的值也就是10。对于引用b来说,C++编译器会将调用函数中临时变量的地址返回给b,而临时变量的数据已经销毁,所以输出b不会得到我们想要看到的结果。
下面我们想如果调用函数中变量时全局变量或者是静态变量时,会发生什么?
int& getB()
{
static int a=10;
return a;
}
int main()
{
int a =getB();
int &b=getB();
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
输出结果:
我们发现输出结果正常,所以我们有下面的结论:
a. 当函数的返回值是引用时,若返回的是普通变量,则不能作为其他引用的初始值。
b. 当函数的返回值是引用时,若返回的是静态变量或全局变量时,能够成为其他引用的初始值。
我们分析一下,函数的返回值是引用时,能不能做为左值??换个说法就是成为左值得条件是什么??
int& getA()
{
int a = 10;
cout << a << endl;
return a;
}
int& getB()
{
static int a = 10;
cout << a << endl;
return a;
}
int main()
{
getA() = 100;
getA();
cout << "--------------------" << endl;
getB() = 100;
getB();
return 0;
}
输出结果:
对于这个结果产生的原因其实上面已经说了,引用返回调用函数中的地址。其实就在于变量的地址能够一直存在。我们可以得出下面的结论:
当函数返回值是引用时,若返回普通变量,它不能作为左值使用。但是,返回的是静态变量或者是全局变量时,可以作为左值使用。
指针的引用以及常量引用
(1). 指针的引用做形式参数格式: Type(数据类型)* &var,指针的引用做形式参数类似于二级指针。
(2). 常量引用:
a. 格式: const +数据类型 +var = 绑定的已定义的变量。
b. 常量引用的作用: 不能通过引用而改变绑定的变量的值。
c. 常量引用的初始化分为2种情况:
//1.通过已定义的变量初始化
int a =10;
const int&b =a;
//2.通过字面值常量初始化
const int&c =10;
我们先看下面的这两个问题?
(1). 普通引用可以用字面值常量初始化吗?
肯定是不可以的,字面值常量C++编译器没有分配内存空间。
(2). 为什么可以通过字面值常量初始化常量引用?
//C++编译器会给10分配内存空间,然后a指向这个内存空间。
const int& a=10;
//C++编译器将b放到符号表中
const int b=10;