目录
1. 引用
1.1 引用特性
1.2 常引用
1.2.1 权限放大
1.2.2 权限缩小
1.3 使用场景
1.3.1 传参
1.3.2 做返回值
1.4 传值和传引用的效率比较
1.5 引用和指针的区别
2. 内联函数
2.1 inline
2.2 特性
在C++中,引入了一个新的概念引用,与传统的定义变量不同,传统的定义变量是新开一份空间来存储数据,而引用则是给一个已经存在的空间起一个别名,它和引用的变量共用同一份空间。
int a = 0;
int& b = a;
int& c = a;
cout << a << ' ' << b << ' ' << c << endl;
cout << &a << ' ' << &b << ' ' << &c << endl;
输出:
0 0 0
000000B0DDAFF6A4 000000B0DDAFF6A4 000000B0DDAFF6A4
从上述的输出可以很明显的看出引用是和被引用的实体共用用一块空间。
常引用就是const关键字修饰的引用,但是我们在使用常引用是会遇到各种报错,其实就是权限放大缩小的问题,所谓权限就是是否具有读或者写的权力,在C++中引用支持权限缩小但不支持权限放大。
首先要说明的是c++是不支持权限放大的,如果出现权限放大就会出现报错。(权限放大就是增加了权限,比如原本变量用const修饰只能读不能修改,你用引用引用它时没有使用const修饰即为可以修改,这就叫做权限放大,这是不被编译器允许的。)
const int a = 0;
//错误写法
int& ra = a; //变量a有const修饰符,说明a只读不可写入,这里没有用const修饰,造成权限放大。
//正确写法
const int& rra = a;
还有一种情况特别容易发生错误,而且犯错的人常常觉得自己没有发生权限放大,但是确确实实发生了,并且发生了报错,这就是会发生类型转换的场景。比如下面的场景。
在C++中权限缩小是被允许的,权限缩小就是比如原来的变量可读可写,你用一个const引用引用这个实体是完全可以的,比如:
int a = 0;
const int& b = a;
传参的一个特别典型的应用就是swap了,因为引用所引用的实体和本体共用的是一个空间,所以引用发生改变其实也就是本体发生改变。所以swap交换变量值就是一个很典型的应用。
void mySwap(int& a, int& b)
{
swap(a, b);
}
int main()
{
int a = 0, b = 1;
cout << a << b << endl;
swap(a, b);
cout << a << b << endl;
}
输出:
01
10
int& count()
{
static int a = 0;
a++;
//...
return a;
}
但是我们在返回引用的时候要特别注意被引用对象的作用域和声明周期,以免导致非凡的访问,比如下面的案例:
//错误案例
int& add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
cout << add(1, 2) << endl;
}
所以这里纵使编译器返回了正确的结果甚至也没有报错,但它依然是一种错误的使用。
以值作为参数或者返回类型,在传参和返回的期间,变量不会直接传递实参或返回变量本身,而传递的是实参或者变量的一份临时拷贝,如果需要拷贝的类型特别大的时候,效率是极低的。而传引用是直接传递实参或返回变量的本身,几乎没有什么消耗。
在语法概念上引用其实是一个别名没有独立的空间,和其引用的实体共用同一块空间。但是在底层的实现上实际上是有空间的,因为引用是按照指针方式来实现的。我们通过引用和指针的汇编代码就可以很容易的看出来。
引用和指针的不同点:
以inline关键字修饰的函数叫做内联函数,编译时C++会在调用内联函数的地方展开,没有函数建立栈帧的开销,提升程序运行的效率。C++期望用inline去替代c语言的宏(因为宏不方便调试,会使代码可读性降低,可维护性差,没有类型的安全检查)
inline是一种空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用将函数体替换函数调用,但会让目标文件变大。
inline对于编译器只是一个建议,不同的编译器关于inline的实现机制有可能不同,一般将函数规模较小的函数不是递归且频繁调用的函数采用inline修饰,否则编译器可能会忽略inline特性。
inline也不支持声明和定义分离分离会导致链接错误,因为inline函数会被展开,没有函数地址,如果声明和定义分开会导致链接阶段找不到函数的地址从而报错。