函數參數之 傳常引用(passed by reference to const)替換 傳值(passed by value)

我們知道,對於一個c++程序員來說,相對於java 等其它面嚮對象語言而言,效率就是c++程序的生命所在,如何使得c++在擁有良好的面向對象特性(抽象,封裝,繼承,多態)特性上,極大的提高c++程序的效率呢?其中最關鍵的技巧就是在函數參數的傳遞過程中 以傳常引用替換傳值(Prefer pass-by-reference-to-const to pass-by-value), 傳常引用替換傳值爲什麼能夠極大的提高c++程序的效率呢?下面請容小生 一一道來,如有爭議 ,歡迎指出。

首先,我們得明白c++程序中 函數參數的傳遞方式
1.傳值(pass-by-value)
2.傳指針(pass-by-pointer)
3.傳引用(pass-by-reference)
有些童靴可能會問到,你不是說,傳常引用嗎,這裏爲什麼是沒有常量性關鍵字 const,不要急,下面,小生會慢慢道來,但是既然提到了const 我們就必須明白,一旦你使用了關鍵字const ,你就告訴了編譯器和團隊中其他程序猿小夥伴們,這個對象不能再修改了的,它是不變的。關於關鍵字const 的詳細講解請參考小生的前面的博客
詳解const

1.函數參數,函數返回值,傳遞過程中的傳值(pass-by-vuale)
(1)效率
c++函數在形參與實參傳遞過程中,默認狀態是以傳值(by value)的形式進行傳遞對象。而各位猿都知道,以傳值方式傳遞參數實際上傳遞的是以實參的副本爲初值,而函數調用端獲取的返回值也只是函數return返回值的一個副本。對於c程序來說,這種方式並沒有什麼不好,但是,各位猿都知道,繼承,封裝,多態,是c++對象的基本特性,那麼問題就來了,這些副本都以對象的拷貝構造函數(copy constructors)產生,這就可能使得以傳值的方式來進行參數傳遞會消耗太多的時間,代價比較昂貴,使得c++程序執行效率比較低,而相對於其它面嚮對象語言如Java ,效率是c++的生命所在,所以,從程序的執行效率來說,對於用戶自定義類型(也就是類),以傳值的方式來進行參數傳遞不可取。

(2)對象切割問題(slicing)
首先我們得明白什麼是對象切割,對象切割是指在c++繼承體系中,當一個派生類對象(derived class)以傳值的方式進行傳遞,並且編譯器把派生類對象視爲一個(base class)基類對象時,此時c++編譯器調用的只是基類的拷貝構造函數,而派生類所特有的特化性質全部被切割掉了,僅僅留下基類對象。也就是說 派生類對象自己所特有的部分全部沒有複製,僅僅複製了基類對象所共有的屬性。而以傳值的方式來進行參數傳遞,對象切割問題無法解決。

在c++程序中,以傳值方式進行參數傳遞,既然存在了(1)程序執行效率低(2)對象切割問題 兩個致命問題,各位猿肯定會思考如何去解決,那麼具體怎麼去解決這個問題呢?各位猿不要心急,請容小生在後面慢慢道來.

2.函數參數傳遞 和 函數返回值以傳常引用的方式(pass by reference to const )的方式進行傳遞
首先我們得明白什麼是引用(reference),對於絕對部分c程序猿來說,c程序中最讓我們頭疼的莫過於對於指針的操作,所以c++標準委員會爲了使c++更容易理解 ,所以引入了引用,但是到最後卻又發現對於c++來說 ,指針又是必不可少的。關於指針和引用的聯繫和區別可以參考小生的博文詳解c++引用與指針的聯繫與區別
簡單來說,對對象的引用就是對原始對象的操作,任何作用於引用對象的操作都等同於對原始對象的操作,所以在函數參數的傳遞過程中 以傳常引用的方式(pass by reference to const),形參和實參都是同一對象,不存在複製,也就不會調用拷貝構造函數,從而能夠有效避免以傳值方式帶來的因爲拷貝構造帶來的低效率和對象切割問題。所以爲了提高c++程序的執行效率和防止對象切割問題,對於用戶自定義類型(也就是你自己寫的類 )來說,在函數參數傳遞過程中儘量以傳常引用代替傳值。

3.傳指針(pass by pointers)
對於指針我想絕大部分程序猿來說又愛又恨吧,愛是因爲它帶來極高的效率,恨的話,我想也沒必要多說了,總之,一句話,你懂得!好了 言歸正傳,就像上文所說的,爲了有效克服c語言中,指針的缺點,c++引入了引用這一概念,如果你對c++編譯器底層理解的足夠深的話,你會發現 ,c++的引用(reference)是以指針來實現的。因此傳引用(pass by reference )通常意味着函數真正傳遞的是對象的指針。所以,如果函數參數類型是內置類型(如 int, char等等)而非用戶自定義類型的話,函數參數以傳值(pass by value)往往比傳引用 (pass by reference)效率更高,當然了,對於像int 這樣的內置類型也沒有繼承這一說法,就更不會發生對象切割了。同理,對於STL中的迭代器和函數對象而言,它們習慣上都被設計爲傳值,所以所以對於內置類型和STL的迭代器 ,函數對象而言,我們選擇傳值 的方式而不是傳引用的方式來進行參數傳遞。因爲內置類型都非常小,因此,肯能某些猿會這麼認爲,是不是所有小型的類型(type)不管是內置類型還是用戶自定義類型只要它足夠小,就可以選擇以傳值的方式來進行參數傳遞呢?這顯然是不合理的。首先,類型對象小並不意味着其拷貝構造函數代價不高。例如 包括STL容器在內的許多對象成員變量只比指針多一些,但是複製這種對象卻必須複製指針所指的每一樣東西,代價非常高昂。其次,即使小型對象的拷貝構造函數 拷貝代價並不高,但在效率上還是有很多爭議。因爲不同的c++編譯器對內置類型和用戶自定義類型的處理方法截然不同,即使它們擁有相同的底層實現(也就是說內置類型和用戶自定義類型擁有相同的 代碼實現)。例如 有些c++編譯器會把內置類型int等放入緩衝區,而不會把由int組成的一個用戶自定義類型INTclass INT{int a;}; 放進緩衝區。很顯然,對於這樣的C++編譯器,內置類型執行速度更快。更何況,對於用戶自定義類型來說,它的大小並不是固定不變的,可能在當前需求中,用戶自定義類型很小,但是隨着需求的改變,類型內部實現可能改變,我們甚至發現,即使c++編譯器的改變都可能改變用戶自定義類型的大小。所以我們可以合理假設傳值“pass by value”的唯一對象就是內置類型,STL迭代器,和函數對象。對於用戶自定義類型來說,不管其大小,儘量以傳常引用(pass by referenc to const)替換傳值。

2.學海拾貝
(1)對於內置類型,STL迭代器,函數對象,不管類型大小,都應該以傳值的方式進行參數傳遞
(2)對於用戶自定義類型(也就是你自己定義的類)來說,爲了提高c++程序的執行效率和避免對象切割問題,我們應該以傳常引用(pass by referenc to const )方式來進行參數傳遞。

3.感謝
心存感恩,才能走得更遠。
感謝:Effective C++ Third Edition

發佈了31 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章