C++引用具體解釋

來源:互聯網
上載者:User

標籤:alt   strong   data   自己的   i++   建構函式   term   輸入   string   

引用是C++中新出現的。有別於C語言的文法元素之中的一個。

關於引用的說明,網路上也有不少。可是總感覺雲遮霧繞,讓人印象不深刻。

今天我就來深入解釋一下引用。並就一些常見的觀點進行說明,最後附帶代碼示範範例予以說明(注意。開發環境是vs2013)。


前面先擺出我的觀點:

1 引用的出現純粹是為了最佳化指標的使用,而提出的文法層面的處理。

2 引用實現原理上全然等價於指標。

3 引用對於傳遞對象參數有很大的最佳化和優點。

4 引用有其局限性,與指標相比,有時候可能與物件導向的設計有衝突。


以下給出我的範例。通過這個範例,我再來慢慢解釋上面的觀點:

void intreference(int& i){printf("[%s]i=%d\n", __FUNCTION__, i);i++;}void objectreference(std::string& str){printf("[%s]str=%s\n", __FUNCTION__, str.c_str());str += 'i';}class mystr :public std::string{public:mystr() :std::string(){}~mystr(){}};void testvirtual(mystr&str){printf("[%s]str=%s\n", __FUNCTION__, str.c_str());}class mytest{public:mytest(){}~mytest(){}virtual void test(){printf("father\n");}};class mysubtest:public mytest{public:mysubtest(){}~mysubtest(){}virtual void test(){printf("hello!\n");}};void testpurevirtual(mytest& test){test.test();}void main(){char* p;int i = 0;refint refintfunc = (refint)intreference;printf("i=%d\n", i);intreference(i);printf("i=%d\n", i);refintfunc(&i);printf("i=%d\n", i);refobj refobjfunc = (refobj)objectreference;std::string obj = "s";printf("str=%s\n", obj.c_str());objectreference(obj);printf("str=%s\n", obj.c_str());refobjfunc(&obj);printf("str=%s\n", obj.c_str());//int& j = i;printf("i %08x,j %08x\n", &i, &i);std::string* pstr = new mystr();//testvirtual(*pstr);//error C2664: “void testvirtual(mystr &)”: 無法將參數 1 從“std::string”轉換為“mystr &”mysubtest t;testpurevirtual(t);getchar();}

範例裡面我給出了兩個引用測試函數和一個變數引用

範例說明了什麼:

1 引用實現原理上全然等價於指標

請注意。函數intreference與函數objectreference是一個引用參數的函數

而函數指標refintfunc與函數指標refobjfunc是一個指標參數的函數指標

對於後者的調用。編譯器會毫不遲疑的將i的地址傳遞給函數

假設引用參數實現原理與指標不全然等價,那麼必定會導致函數調用出現故障

但結果卻非常有趣,我發現兩種方式,效果全然同樣,沒有不論什麼差異。

以下是執行時的反組譯碼:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >

從反組譯碼能夠清晰的看到,對於直接進行引用參數函數調用。和使用指標參數調用。兩者的彙編代碼全然沒有什麼差別

引用在實現的時候,傳遞的就是一個指標給函數!!

不不過對於單一資料型別如此,對於複雜資料類型這也是相同的:

能夠看到,兩者都是將obj的地址作為參數,放入到了eax,然後再推送到棧中去了

也就是說,在實現層面上面,兩者是等同的

那麼對於局部,非參數傳遞的引用呢?

以下是局部引用j和其引用對象i賦值時的反組譯碼:

注意第一條紅線,j是有自己的棧空間地址的!並不是如同網路上所說的別名。不佔用空間,等價等等。不是這種!

它仍然要佔空間,佔一個指標大小的空間。假設i和j是char和char引用,那麼j佔用的空間甚至比i還大!

在賦值的時候。系統將i的地址給eax。然後再通過eax寄存器將地址傳入j,注意dword ptr,這表示指標j!

這和我前面提到的觀點:引用實現原理上全然等價於指標 是全然一致的。

>

<

2 既然它在實現層面上全然等價於指標。那為什麼還會有引用?

這就要回到我前面提出的第一個觀點:引用的出現純粹是為了最佳化指標的使用,而提出的文法層面的處理

假設這裡使用指標。就會很麻煩!

首先,假設函數的參數是指標。開發人員就必需要要驗證指標!這個差點兒是無法避免的情況!

否則指標一旦為空白,整個程式必定崩潰。

可是引用就避免了這個麻煩——通過文法層面上的幹預——使得使用者無法顯式的傳遞null 指標到函數中去

假設有null 指標或者野指標,崩潰僅僅會發生在函數外部,而非內部。

其次。輸入.比輸入->更加讓開發人員開心一些,不論是長度還是安全性上,指標式的成員函數調用,總讓人心驚膽顫

因此,引用全然是一種文法層面的處理。就是C++中的私人成員變數一樣,僅僅是從文法上阻止使用者去顯式訪問——實際上能夠利用指標,強制從記憶體中讀寫該變數。

當然引用不只不過這樣,之所以物件導向要增加引用,另外一個作用還在於:

假設參數純粹是一個對象,那麼意味著程式須要頻繁的在棧上面構造和析構對象。

而引用成功的攻克了這個問題。能夠讓開發人員決定要不要在棧上面構造對象並自己主動析構它。

這樣導致效率極大的提升了——非常多複雜的對象。其建構函式和複製建構函式可能異常複雜和耗時。

同一時候,另外一些對象可能並不希望調用者使用它們的建構函式。比方單例對象!

而引用非常好的攻克了這個矛盾。

3引用有沒有限制?答案是有!

限制在哪裡?我們知道。物件導向設計中有介面這個概念,而C++與之關聯的是虛函數。

我們常常會持有一個父類的指標,而在當中填入各種子類的對象,然後通過虛函數去調用相應的子類介面實現。

可是這裡使用引用卻有限制。僅僅能在聲明為父類引用的時候。使用子類,而無法在聲明為子類引用的時候使用父類。

指標卻能夠不受此限制,進行自由的轉化(當然這是有風險的!

以下給出了一個示範範例:

對於mystr和函數testvirtual,假設傳入一個父類對象(實際上還是一個子類,僅僅是是一個父類指標),在文法上這是被禁止!

對於mytest和mysubtest以及函數testpurevirtual,這樣又是能夠的。

這種限制要求開發人員在設計的時候就必須很仔細,事先想好介面的統一性。否則後面代碼就有的改了

當然,這樣也有優點,能夠避免一些問題。比方null 指標或者對象不匹配異常(將一個非mytest或者其子類的對象指標強制轉化過來。此時調用必定崩潰。)

<pre code_snippet_id="1639674" snippet_file_name="blog_20160408_34_61346" name="code" class="cpp">class mystr :public std::string{public:mystr() :std::string(){}~mystr(){}};void testvirtual(mystr&str){printf("[%s]str=%s\n", __FUNCTION__, str.c_str());}class mytest{public:mytest(){}~mytest(){}virtual void test(){printf("father\n");}};class mysubtest:public mytest{public:mysubtest(){}~mysubtest(){}virtual void test(){printf("hello!\n");}};void testpurevirtual(mytest& test){test.test();}void main(){int i = 0;refint refintfunc = (refint)intreference;printf("i=%d\n", i);intreference(i);printf("i=%d\n", i);refintfunc(&i);printf("i=%d\n", i);refobj refobjfunc = (refobj)objectreference;std::string obj = "s";printf("str=%s\n", obj.c_str());objectreference(obj);printf("str=%s\n", obj.c_str());refobjfunc(&obj);printf("str=%s\n", obj.c_str());int& j = i;printf("i %08x,j %08x\n", &i, &j);std::string* pstr = new mystr();//testvirtual(*pstr);//error C2664: “void testvirtual(mystr &)”: 無法將參數 1 從“std::string”轉換為“mystr &”mysubtest t;testpurevirtual(t);getchar();}
最後給出執行結果的:

能夠看到。結果充分說明了引用事實上就是指標

這裡補充說明一下i和j的問題:

當我聲明了j的時候,能夠看到函數棧的大小


而沒有聲明j的時候。函數棧明顯變小了


小了12位元組,非常奇怪,好像和指標的大小不一致啊

沒有關係,我再聲明一個指標,我們再看看


看到沒有?棧又恢複到了14c了。而我僅僅是聲明了一個char*p,而且沒有做不論什麼調用。

這說明j是佔領空間的,大小正好是一個指標!!


 

C++引用具體解釋

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.