----引用的地址:

    由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。

 

----引用的赋值

    引用一但初始化,就不在能够被指向其它的目标,虽然编译不会出错,但操作是不起作用的,实际上还是指向最先指向的目标。
int a=10;  
int b=20;  
int &rn=a;  
rn=b;//把引用指向另一个目标----变量b 
上面代码中的rn=b实际在计算机看来就是a=b,所以修改的还是a的值。

    void修饰是不能够声明引用的,引用是不能够声明数组的,即不能够声明引用数组,因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

 

----返回类成员引用:

    可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。


----引用与一些操作符的重载:
    流操作符<<和>>:这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。

    赋值操作符=,这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

#include <iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main()
{
    put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10; 
    put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=10;
    cout<<vals[0]; 
    cout<<vals[9];
}
int &put(int n)
{
    if (n>=0 && n<=9 ) return vals[n];
    else { cout<<"subscript error"; return error; }
}

    在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

 

----引用和多态:
    引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。
class  A;
class  B:public A{……};
B  b;
A  &Ref = b; // 用派生类对象初始化基类对象的引用
Ref 只能用来访问派生类对象中从基类继承下来的成员,是基类引用指向派生类。如果A类中定义有虚函数,并且在B类中重写了这个虚函数,就可以通过Ref产生多态效果。

 

----引用与函数返回值

1、

#include <iostream>   
#include <string>   
using namespace std;  

float c; 
float test(float,float); 
void main(int argc,char* argv[])     

    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  在上面的代码中我们可能以为函数返回的就是c变量,这么想可能就错了,普通情况下我们在函数内进行普通值返回的时候在内存栈空间内其实是自动产生了一个临时变量temp,它是返回值的一个副本一个copy,函数在return的时候其实是return的这个临时产生的副本。

2、把返回值赋给引用:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float test(float,float); 
void main(int argc,char* argv[])     

    float &pn=test(3.0f,1.2f);//警告:返回的将是临时变量,pn引用将成为临时变量的别名! 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  float &pn=test(3.0f,1.2f);这句在bc中能够编译通过,因为bc扩展设置为临时变量设置引用,那么临时变量的生命周期将和引用的生命周期一致,但在vc中却不能通过编译,因为一但test()执行过后临时变量消失在栈空间内,这时候pn将成为一个没有明确目标的引用,严重的时候会导致内存出错。

3、返回引用给变量的情况:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,char* argv[])     

    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种返回引用给变量的情况下,在内存中,test()所在的栈空间内并没有产生临时变量,而是直接将全局变量c的值给了变量pn,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,程序的可读性也是比较好的。但是仅限于返回的变量是栈中分配的空间,或全局变量,否则返回的依然是临时变量。一般对于内置类型,可以使用第一种情况直接返回副本。

4、最后的一种情况是函数返回引用,并且发值赋给一个引用的情况:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,char* argv[])     

    float &pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种情况同样也不产生临时变量,可读和性能都很好,但有一点容易弄错,就是当c是非main的局部变量或者是在堆内存中临时开辟后来又被fee掉了以后的区域,这种情况和返回的指针是局部指针的后果一样严重,会导致引用指向了一个不明确的地址。

----其他用法:

    在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。