前两次总结了指针用法,发现引用是不是跟指针有某种关系呢?
经过认真总结发现引用是一种特殊的指针,特殊性如下:
引用是一种变相的指针,不过有更多的限制:
1,必须定义时初始化.
2,所引用的对象不能更改
3,使用时类似于对象.
指针使用->调用 引用使用. 跟对象使用方式一样。
下面从下面代码分析出引用和指针调用联系:
// testreference.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
void func (int &ia) {
ia=100;
}
void func1 (int ia) {
ia=100;
}
void func2(int *a){
(*a)=100;
}
int main(int argc, char* argv[])
{
int ix=99;
func(ix);
func1(ix);
func2(&ix);
cout<<"ix: "<<ix<<endl;
}
在vs 2010 反汇编代码如下:
func(ix);//引用调用
00411715 lea eax,[ix]
00411718 push eax
00411719 call func (411258h)
0041171E add esp,4
func1(ix);
00411721 mov eax,dword ptr [ix] //把[ix]地址的值赋给eax。
00411724 push eax
00411725 call func1 (41125Dh)
0041172A add esp,4
func2(&ix);//指针调用
0041172D lea eax,[ix]
00411730 push eax
00411731 call func2 (411262h)
00411736 add esp,4
可以看到func(&ix) 和 func反汇编代码一样。所以引用和指针调用方式一样。
再看下面程序:
class A {
double *m_pDb;
};
class B {
double &m_db;
public:
B(double db = 0) : m_db(db) {}
};
int main(int argc, char* argv[])
{
cout << "size of class A:"<<sizeof(A) << endl;//4
cout << "size of class B:"<<sizeof(B) << endl;//4
return 0;
}
运行结果为:
size of class A:4
size of class B:4
Press any key to continue
所以引用本质上也是一个指针。
再来看一个程序:
#include <string>
using namespace std;
int main()
{
int a=100;
int* b = &a;
int& c = a;
a++;
(*b)++;
c++;
b++;
return 0;
}
反汇编代码:
int a=100;
00411DFE mov dword ptr [a],64h
int* b = &a;
00411E05 lea eax,[a]
00411E08 mov dword ptr [b],eax
int& c = a;
00411E0B lea eax,[a]
00411E0E mov dword ptr [c],eax
a++;
00411E11 mov eax,dword ptr [a]
00411E14 add eax,1
00411E17 mov dword ptr [a],eax
(*b)++;
00411E1A mov eax,dword ptr [b] //b为指针变量所以存放的时地址
00411E1D mov ecx,dword ptr [eax] //通过刚才得到的地址得到数放到ecx(间接寻址)
00411E1F add ecx,1
00411E22 mov edx,dword ptr [b]
00411E25 mov dword ptr [edx],ecx
c++;
00411E27 mov eax,dword ptr [c]
00411E2A mov ecx,dword ptr [eax]
00411E2C add ecx,1
00411E2F mov edx,dword ptr [c]
00411E32 mov dword ptr [edx],ecx
b++;
00411E34 mov eax,dword ptr [b] //因为int指针所以下面eax+4代表指针b++.
00411E37 add eax,4
00411E3A mov dword ptr [b],eax
可以看到 C++ 与(*b)++ 的反汇编代码是一样的。
int* b = &a;
int& c = a;
这两个反汇编代码也是一样。
00411E05 lea eax,[a] 把a 变量地址赋给 eax 而不是把a 变量的值。
00411E11 mov eax,dword ptr [a] 这个才是把a 的值赋给eax 。
注意mov 和lea 的区别。
引用的作用(参照 http://topic.csdn.net/t/20040101/13/2624096.html )
1. 反汇编模式下可以看到引用的本质就是指针,在机器代码层次上引用没有另外的区别于指针的实现方式,所以我认为引用更多的是面向抽象层次的使用,即我们在编写C++ 代码的时候做一些限制,比如引用不能为NULL。编译器一旦把引用“翻译”成机器代码就和指针没什么区别。
2. 引用具有指针的能力,但是调用引用传递的函数时,可读性却比指针传递好。
引用具有传值方式函数调用语法的简单性与可读性,但是威力却比传值方式强!
3. 引用最大的贡献就是消除了指针的不安全,并且给操作浮重载带来巨大的方便 。
4. 对引用要注意一定不能 //char& rc = pc; //cannot convert from 'char *' to 'char &',
其中char g='g'; char *pc = &g; 要char & rc=*pc 才正确。 cout<<rc<<endl; 输出 g
5.
引用有个特殊功能,就是当一个临时对象绑定到引用,这个临时对象的生命期,将延长至这个引用的生命期结束时指针却没有这个功能。
可以通过下面代码来理解:
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
int i;
A(){i=50; cout <<"A() construct"<<endl; }
~A(){i=0; cout <<"~A() disconstruct"<<endl; }
};
void fun(A* pa)
{
cout << "void fun(A*) " <<endl;
cout <<pa-> i <<endl;
}
void fun(const A& ra)
{
cout << "void fun(const A&) " <<endl;
cout <<ra.i <<endl;
}
int main()
{
const A& ra=A(); //将一个临时对象帮定到常量引用ra
cout<<"const A& ------------------"<<endl;
A* pa=(&A()); //将另临时对象的地址赋给pa,在对临时对象去址后,临时对象就被析构了,生命结束,完成使命 new A()
cout<<"A* pa=(&A()) ------------------"<<endl;
fun(pa); //输出的是0,临时对象已不存在,这里只是残留的痕迹
fun(ra); //但这里输出的是50,发现没有,那个临时对象还存在,因为没有调用析构函数 //引用的调用方法也不一样。如果去掉 fun(const A& ra)会出现cannot convert parameter 1 from 'const class A' to 'class A *'错误。
A* pap=new A();//这个就不是临时对象了。
fun(pap);
A objA();
cout<<"A objA() ------------------"<<endl;
//fun(objA)或者fun(&objA);none of the 2 overloads can convert parameter 1 from type 'class A (__cdecl *)(void)'
}
输出结果为:
A() construct
const A& ------------------
A() construct
~A() disconstruct
A* pa=(&A()) -------------//这个在~A()disconstruct 后说明A*pa=(&A())是临时对象。
void fun(A*)
0
void fun(const A&)
50//输出50 绑定const A& ra=A(); 将延长至这个引用的生命期结束时 指针却没有这个功能。
A() construct
void fun(A*)
50
A objA() ------------------
~A() disconstruct//析构在函数-----后这不是临时对象
Press any key to continue