在很多的教科书上都写了引用是一个对象的别名,那么别名是什么意思又是怎么实现的呢?在看了一位大侠写的深入分析C++引用后,豁然开朗,在这里分享给大家:
其实确实是有别名这种东西的。c++标准规定,引用可以占内存也可以不占,取决于编译器实现。占内存的实现就是使用指针了。很多编译器为了简单都全部采用这种实现方法。不占内存的实现呢,是引用和引用的对象在同一个函数中的时候。局部变量名其实是一个相对于栈基址的偏移量,这种情况下你定义这个局部变量的引用,编译器可以直接给这个引用赋同样的偏移值,这样引用名和变量名完全没有区别。这就是别名的含义了。不过引用作为函数参数的时候确实只有用指针实现这种方法了。
在大多数编译器中,引用其实就是 c++ 中的常量指针,它也拥有内存空间,最关键的是引用是一种会被编译器自动解引用的指针,引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须有所指。
void main()
{
/
//引用说白了就是一个常量指针 const int*
/引用的示例//
int i=10;
int& j=i;
j++;
cout<<i<<","<<j<<endl;
cout<<&i<<","<<&j<<endl;
/
int i1=10;
int* const j1=&i1;
(*j1)++;
cout<<i1<<","<<*j1<<endl;
cout<<&i1<<","<<&(*j1)<<endl;
/此处的&(*j1)=&j;
/
}
那么在我们在类的传递函数中,经常将类类型的参数声明为引用类型,如下:
class Test
{
public:
Test(int x=0,int y=0);//构造函数
Test(Test& T);//拷贝构造函数
Test operator=(Test T);//赋值函数
/****************************************************** /
//Test& operator=(Test& T);//赋值函数
/******************************************************/
virtual ~Test();
public:
int x;
int y;
friend Test operator+(const Test& A,const Test& B);
};
Test::Test(int x,int y)//构造函数
{
this->x=x;
this->y=y;
cout<<"X"<<endl;
}
Test::Test(Test& T)//拷贝构造函数
{
cout<<"Test"<<endl;
}
Test Test:: operator=(Test T)
{
cout<<"="<<endl;
return T;
}
/****************************************/
/*
Test& Test:: operator=(Test& T)
{
cout<<"="<<endl;
return T;
}
*/
/****************************************/
Test::~Test()
{
cout<<"this ~Test"<<endl;
}
void main()
{
Test a,b;
a=b;
}
当运行这段代码的时候,会发现拷贝构造函数被调用两次。
当你使用"="的引用版本的时候你会发现,拷贝构造函数没有被调用过。
此时就发现为甚两种版本,拷贝构造函数被调用的次数不同呢,其实本质上是引用在作怪。
引用从官方的说法上来看,就是给一个对象起了一个别名。其实我感觉从本质上来说,引用使这个对象突破了作用域的限制,让这个对象能够传入到另一个作用域中,被使用。
以上面的代码为例:
Test& Test:: operator=(Test& T)
{
cout<<"="<<endl;
return T;
}
当调用发生的时候即:A=B; 就等价于 A.operator=(B);因为引用的使用,进入函数中就省去了拷贝构造这一部分,此时B这个对象的作用域就扩展到
{
cout<<"="<<endl;
return T;
}
中。
这样做的好处是,减少了对象在函数传递过程中的拷贝(因为对象中的数据量可能很多很大),减少了开销。当然在函数中进行的一些修改也会反应到被传递进来的B上,因此当在函数中不希望对B进行修改的时候,尽量加上const 修饰