C++中的 引用&

引用的概念

引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名=目标变量名;
例:char ch;
       char &rp=ch;
1) 引用仅是变量的别名,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址.
2)表达式中的取地址符&不再是取变量的地址,而是用来表示该变量是引用类型的变量。
3) 定义一个引用时,必须对其初始化。

引用作为函数参数和函数返回

首先来看一个函数

int fun(int x){
    int y=2*x;
    return y;
}
该函数会先构造一个x的复制,然后用这个复制参与函数体中的运算。
运算完后,函数会构造一个运算结果y的复制,然后将这个复制返回。

也就是说,函数的调用中会进行两次复制,第一次是对参数进行复制,用这个复制品去运算从而保证原参数不会被改动;第二次是对返回值进行复制,从而释放作为局部变量的返回值。这一点可以通过下面代码来验证:

class CA {
public:
	CA() { cout << "CA()" << endl; }
	CA(CA& b) { cout << "CA(CA& b)" << endl; }
	~CA() { cout << "~CA()" << endl; }
};
CA fun(CA a) {
	return a;
}
int main() {
	CA a;
	cout << "调用函数fun()" << endl;
	fun(a);
}

得出结果:

函数调用了两次复制构造函数,两次析构函数。首先对参数a进行复制,产生复制品cp1,然后用cp1参与运算,得到返回值结果y,然后对y进行复制,产生复制品cp2, 真正返回的就是cp2. 被析构的分别是cp1和y.

而引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递,如果有大块数据作为参数传递的时候,采用的方案往往是指针,因为这样可以避免将整块数据全部压栈,提高程序的效率。但是现在(C++中)又增加了一种同样有效率的选择(在某些特殊情况下又是必须的选择),就是引用。
(1)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(2)使用指针作为函数的参数也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用\"*指针变量名\"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
(3)指针参数不能引用,只会复制。所以要用函数改变某个指针变量时,需要使用该指针的指针,即二级指针。

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式:const  类型标识符  &引用名 = 目标变量名;

接着上面的例子,我们把参数改成引用的形式

CA fun(CA& a) {
	return a;
}

得到结果:

也就是说参数不再复制,而是直接用指向a的引用去参与运算,在不加const的情况下,这也意味着 a 可以被函数体任意修改而且修改是永久有效的。        计算得出返回值y后,仍然对y进行复制,把复制品cp返回,并且析构y.

返回对象会涉及到生成返回对象的副本,因此,返回对象的时间成本包括了调用复制构造函数来生成副本所需的时间和调用析构函数删除副本所需的时间。返回引用可以节省时间和内存。直接返回对象与按值传递对象类似,他们都生成临时副本。同样,返回引用与按引用传递对象类似,调用和被调用的函数对同一个对象进行操作。   

把返回改成引用的形式:

CA& fun(CA a) {
	return a;
}

结果也是一次复制构造和一次析构。即仅有参数a的复制以及复制品的析构。最后返回的是返回值的引用。由于返回值中仅有a,不包含局部变量,所以是安全的。

也可以把参数和返回都改成引用的形式,不再赘述。

  并不是总是可以返回引用的,函数不能返回在函数中创建的临时对象的引用,因为当函数结束时,临时对象将消失,因此这种引用是非法的,在这种情况下,应返回对象,以生成一个调用程序可以使用的副本。

此外,在等号传递时,若没有引用则是调用复制构造函数来产生副本的过程,若使用引用则会直接指向该对象,而不产生复制。

CA a;
cout<<"---"<<endl;
CA b=a;//调用一次复制构造函数
cout<<"---"<<endl;
CA& c=a;//不调用

结果:

引用和多态

引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

class  A;
class  B:public  A{ ... ... }
B  b;
A& ref = b;//用派生类对象初始化基类对象的引用

ref 只能用来访问派生类对象中从基类继承下来的成员,是基类引用指向派生类。如果A类中定义有虚函数,并且在B类中重写了这个虚函数,就可以通过ref产生多态效果。这与指针是一样的,不再赘述。

引用与指针的不同特性

引用不可以为空,当被创建的时候,必须初始化,而指针可以是空值,可以在任何时候被初始化。
指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
"sizeof引用" 得到的是所指向的变量(对象)的大小,而 "sizeof指针" 得到的是指针本身的大小;
指针和引用的自增(++)运算意义不一样;
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏;

猜你喜欢

转载自blog.csdn.net/sinat_38972110/article/details/82935329
今日推荐