❥关于C++之引用

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❥关于C++之引用相关的知识,希望对你有一定的参考价值。

引用的本质是指针

int rats = 101;
int& alias = rats;
int* prats = &rats;
——————————————————————————
alias <=> *prats <=> rats
&alias <=> prats <=> &rats

alias和*prats都可以同rats互换,&alias和prats都可以同&rats互换。从这一点来说,引用看上去很像伪装表示的指针。
也就是说:

int & alias = rats;
实际上是下述代码的伪装表示:
int * const pr = &rats;// 【指针常量】

由于pr是指针常量(指向不能变,所指值能变),所以pr所指地址不变,即一直跟踪rats,能通过pr改变rats的值,直接改变rats的值也同步到(*pr),所以和引用的本质是一样的。
引用也可以再赋值,引用效忠的对象也可再赋值,但总之引用是作为别名一直效忠于rats变量,不易主!
引用是引用的对象的别名!


int rats;
int& rodents = rats; // 使rodents成为rats的别名

引用更接近指针常量,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它(不易主)。——地址与地址上存的值总相同。

假设程序员试图这样做:

int rats = 101;
int* pt = &rats;
int& rodents = *pt; // 一直效忠rats
int bunnies = 50;
pt = &bunnies;

将rodents初始化为*pt使得rodents指向rats。接下来将pt改为指向bunnies,并不能改变这样的事实,即rodents引用的是rats。

下图直观演示了按值传递与按引用传递的区别:

示例:用三种方式,交互两个int值

#include <iostream>
void swapr(int& a, int& b);   // a, b are aliases for ints
void swapp(int* p, int* q);   // p, q are addresses of ints
void swapv(int a, int b);     // a, b are new variables
int main() 
	using namespace std;
	int wallet1 = 300;
	int wallet2 = 350;
	cout << "wallet1 = $" << wallet1;
	cout << " wallet2 = $" << wallet2 << endl;
 
	cout << "Using references to swap contents:\\n";
	swapr(wallet1, wallet2);   // pass variables
	cout << "wallet1 = $" << wallet1;
	cout << " wallet2 = $" << wallet2 << endl;
 
	cout << "Using pointers to swap contents again:\\n";
	swapp(&wallet1, &wallet2); // pass addresses of variables
	cout << "wallet1 = $" << wallet1;
	cout << " wallet2 = $" << wallet2 << endl;
 
	cout << "Trying to use passing by value:\\n";
	swapv(wallet1, wallet2);   // pass values of variables
	cout << "wallet1 = $" << wallet1;
	cout << " wallet2 = $" << wallet2 << endl;
	return 0;

void swapr(int& a, int& b) 
	int temp;
	temp = a;
	a = b;
	b = temp;

void swapp(int* p, int* q) 
	int temp;
	temp = *p;
	*p = *q;
	*q = temp;

void swapv(int a, int b) 
	int temp;
	temp = a;
	a = b;
	b = temp;

wallet1 = $300 wallet2 = $350            <<原始数据
Using references to swap contents:       
wallet1 = $350 wallet2 = $300            <<值被交换了
Using pointers to swap contents again:
wallet1 = $300 wallet2 = $350            <<值又被交换了
Trying to use passing by value:
wallet1 = $300 wallet2 = $350            <<交换失败
int main() 
	double x = 3.0;
	cout << cube(x);
	cout << " = cube of " << x << endl;
	cout << refcube(x);
	cout << " = cube of " << x;
	return 0;

double cube(double a) 
	a *= a * a;
	return a;

double refcube(double& ra) 
	ra *= ra * ra;
	return ra;

27 = cube of 3
27 = cube of 27

为何要返回引用:
计算关键字return后面的表达式,并将结果返回给调用函数。从概念上说,这个值被复制到一个临时位置,而调用程序将使用这个值。

double m = sqrt(16.0);
cout << sqrt(25.0);

在第一条语句中,值4.0被复制到一个临时位置,然后被复制给m。在第二条语句中,值5.0被复制到一个临时位置,然后被传递给cout。

dup = accumalate(team, five);

如果accumulate()返回一个结构,而不是指向结构的引用,将把整个结构复制到一个临时位置,再将这个拷贝复制给dup。但在返回值为引用时,将直接把team复制到dup,其效率更高。

返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元引用。应避免编写下面的代码:

const free_throws & clone2(free_throwns & ft) 
    free_throwns newguy; // first step to big error
    newguy = ft; // copy info
    return newguy; // return reference to copy

该函数返回一个指向临时变量(newguy)的引用,函数运行完毕后它将不再存在。同样,也应避免返回指向临时变量的指针。
为避免这种问题,最简单的方法是,返回一个作为参数传递给函数的引用。作为参数的引用将指向调用函数使用的数据,因此返回的引用也将指向这些数据。如下代码就是这样做的:

free_throws& accumulate(free_throws& target, const free_throws& source) 
	target.attempts += source.attempts;
	target.made += source.made;
	return target;

示例:不注意上面要避免的后果:

int& test(int&);
int main() 
	int x = 6;
	int& y = test(test(x));// test()函数运行完后返回的临时引用消失!因为临时引用无变量承载!
	cout << y;
	return 0;

int& test(int& i) 
	int tmp = 2 * i;// 局部临时变量
	return tmp;// 块结束后,tmp变量被系统回收消失

有些系统或IDE输出:
-1717986920
有些报错:
Segmentation fault
另一些报错:
warning: reference to local variable 'tmp' returned [-Wreturn-local-addr]

当返回值是引用类型时,会返回该变量的地址。当返回值不是引用型时,编译器会专门给返回值分配出一块临时内存。
总之,绝不能将一个指向局部变量的引用类型值作为函数的返回值!
同样,将一个指向局部变量的指针作为函数的返回值是也有问题的!
程序试图引用已经释放的内存,就是大问题。

将const用于引用返回类型及参数:
避免在设计中添加模糊的特性,因为模糊特性增加了犯错的机会。

accumlate(dup, five) = four;// 直接将函数返回的引用赋值!

假设您要使用引用返回值,但又不允许执行像给accumulate()赋值这样的操作,只需将返回类型声明为const引用:

const free_throwns & accumlate(free_throwns & target, const free_throwns & source);

参数source类型也声明为const,让它成为“只读”。

当然,有时省略const确实有道理,在讨论的重载运算符<<就是一个这样的例子。

以上是关于❥关于C++之引用的主要内容,如果未能解决你的问题,请参考以下文章

删除 Visual C++ 菜单栏

❥关于C++之引用

c++中的左值和右值,右值引用到底是啥?关于引用这一节看得很迷糊。

依赖注入在C ++中是否有用

C++原生指针,引用与智能指针

C++学习(二六四)RTTI