引用是给已经存在的变量取一个别名,也就是说编译器不会给引用的变量重新开辟新的空间,引用变量和引用实体公用一块空间,采用形式为:类型& 引用变量名(对象名) = 引用实体,在代码中的形式如下:
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%pn", &a);
printf("%pn", &ra);
}
注意:引用类型必须和引用实体是同种类型的。
1.2、引用特性与常引用引用有以下特性:引用在定义时必须初始化、一个变量可以有多个引用、引用一旦引用了一个实体,就不能再引用其它实体,代码如下所示:
void TestRef()
{
int a = 10;
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a;
printf("%p %p %pn", &a, &ra, &rra);
}
在常引用中还涉及如下几种情况:
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量,引用权限的放大
const int& ra = a;//引用权限的平移,编译运行正常
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;//两边都为常量,编译运行正常
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d; //引用权限的缩小,编译运行正常
}
总结:常引用可以做引用权限的平移、缩小,但不能做放大的操作。
1.3、引用使用场景引用可以做参数,这样形参也可改变实参案例如下:
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
引用也可以做返回值,代码如下:
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
为什么需要用static来修饰int n?若不用static来修饰则n存放在栈区,出栈内存空间被释放,而static修饰的变量存放在静态区,静态区的变量在程序运行结束时由系统释放空间,故没问题。
总结:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已
经还给系统了,则必须使用传值返回。
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,这使得传值的效率很低,具体测试如下:
#includestruct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A& a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; }
由测试可见,两者效率相差甚远。
在语法角度,引用是一个别名,没有独立的空间,和引用实体公用一块空间,指针开了4or8个byte空间如下所示:
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<
从底层实现角度,引用实际是有空间的,因为引用是按照指针方式来实现的,如下所示:
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
引用反汇编
指针反汇编,由此可以看出两者在底层的实现是一致的。
以下为引用和指针的不同点:
1、 引用在定义时必须初始化,指针没有要求;
2、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体;
3、没有NULL引用,但有NULL指针;
4、在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节);
5、引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小;
6、有多级指针,但是没有多级引用;
7、访问实体方式不同,指针需要显式解引用,引用编译器自己处理;
8、 引用比指针使用起来相对更安全。
2、内联函数
2.1、内联函数的概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,如下为普通函数与内敛函数的汇编代码:
2.2、内联函数的特性
由2.1图所知,内敛函数没有压栈的开销,是一种以空间换时间的做法,但内敛函数不适合用于码很长或者有循环/递归的函数,因为会引起编译指令的暴增,inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略内联,inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。



