前兩次總結了指針用法,發現引用是不是跟指針有某種關係呢?
經過認真總結髮現引用是一種特殊的指針,特殊性如下:
引用是一種變相的指針,不過有更多的限制:
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