声明引用
int a = 3;
int& b = a; //b是a的引用,a,b是同一变量
b = 4; //这时候a也变为4
int c = 2;
b = c; //将c赋值给a或者b,而不是把b作为c的引用,引用一旦被声明同时被初始化,引用对象不可更改
引用的实质是指针的简化运用版,上面的代码等价为
int a = 3;
int* const b = &a; //常指针,b的位置不能更改,相当于引用不能更改自身引用对象 *b = 4;
所以引用省去了声明时的取地址操作和对变量内存地址引用时的解引用操作,而且引用不能随意被更改,站在指针的角度上说,引用的意义等价于一个常指针,也就是不能改变自己指向位置的指针。
引用在作为参数传递的时候,传递的是实参本身,C++里面类设计经常要传递整个类作为参数,但如果在不必要的时候使用传统的按值传递,效率损失可见一斑:
class foo {
public:
...
int a;
double b;
string c;
};
void bar(foo f)
{
//输出abc
}
比如这个函数bar,传参的时候是按值传递,也就是实参的所有数据被重新复制到参数f里,构造了一个新对象f,它只是输出参数f的abc成员,所以必要重新构造一个临时对象f,这样造成了效率的损失,解决方法有2种:
void bar1(foo* f)
{
cout<<f->a<<f->b<<f->c;
}
var bar2(foo& f)
{
cout<<f.a<<f.b<<f.c;
}
这2种方式的运作机制是相同的,但是后者要更简洁和优美一些,特别是在操作多层间接指针的函数里面,直接使用指针的话,过多取地址&和解引用*容易造成代码混乱,降低可读性。
friend Complex operator +(Complex&, Complex&);
在这个重载语句中 "Complex&"的"&"作用是什么? 假如不写"&"有什么影响?
在类中定义时是不带参数如friend Complex operator +(Complex&,Complex&);
在实现中再定义friend Complex operator +(Complex& temp1,Complex& temp2);
这个重载语句中 "Complex&"的"&"作用是表示调用函数时传递参数是以引用方式传递的,这样在函数中操作的对象就是调用外面的那个对象本身。
假如不写"&"的话,其影响有两个。
1.当你想对这个参数的对象进行修改的话,在函数返回后,这个修改的值不会改变。
2.在做参数传递的时候,不加"&"的话,传递的过程中,会创造一个临时对象,这样会造成一定的效率浪费。
在编程中,函数分声明和定义。声明只是告诉编译器,存在这个函数,这个时候可以不带参数名称。而定义的时候,就必须实行函数本体,这个时候就必须带上参数的名称了。
引用“& " 的用法与指针的用法
void swap(int& p1, int& p2)
{
undefined
int p;
p = p1;
p1 = p2;
p2 = p;
}
为在程序中调用该函数,则相应的主调函数的调用点处,直接以变量作为实参进行调用即可,而不需要实参变量有任何的特殊要求。如:对应上面定义的swap函数,相应的主调函数可写为:
main()
{
int a,b;
cin>>a>>b; //输入a,b两变量的值
swap(a,b); //直接以变量a和b作为实参调用swap函数
cout<<a<<" "<<b; //输出结果
}
上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。
由例子可看出:
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
碰到以下情况例如
intStackEmpty(SqStack s)
{
if(s.top == s.base)
return TRUE;
return FALSE;
}
int Push(SqStack& s, SElemType e)
{
if(s.top - s.top>= s.stacksize)
{
s.base=(SElemType*)realloc(s.base,(s.stacksize + STACKINCREMENT)*
sizeof(SElemType));
if(!s.base)
exit(OVERFLOW);
s.top = s.base + s.stacksize;
s.stacksize += STACKINCREMENT;
}
*s.top++ = e;
return TRUE;
}
int Pop(SqStack& s, SElemType& e)
{
if(s.top == s.base)
return FALSE;
e=*--s.top;
return TRUE;
}
在堆栈操作中,有的不用&,有的要用&,什么时候使用引用 ?
1、首先我们要看看什么时候必须使用引用:
- 流操作符 << 和 >> 、赋值操作符 = 的返回值
- 拷贝构造函数的参数、赋值操作符 = 的参数
2、其它下面的情况都是推荐使用引用,但是也可以不使用引用。如果不想使用引用,完全可以使用指针或者其它类似的东西替代:
- 异常catch的参数表
- 大对象作为参数传递
- 返回容器类中的单个元素
- 返回类数据成员(非内建数据类型成员)
- 返回其它持久存在的,且获得者不负责销毁的对象