日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

条款20 宁以pass-by-reference-to-const替换pass-by-value

發布時間:2023/12/14 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 条款20 宁以pass-by-reference-to-const替换pass-by-value 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

總結:

1、盡量以pass-by-reference-to-const替換pass-by-value。前者更高效且可以避免切斷問題。

2、這條規則并不適用于內建類型及STL中的迭代器和函數對象類型。對于它們,pass-by-value通常更合適。


??????? 缺省情況下,C++以傳值方式將對象傳入或傳出函數(這是一個從C繼承來的特性)。除非你另外指定,否則函數的參數就會以實際參數的副本進行初始化,而函數的調用者會收到函數返回值的一個復件。這個復件由對象的拷貝構造函數生成,這就使得傳值成為一個代價不菲的操作。例如,考慮下面這個類繼承體系:

class Person { public:Person(); // 為求簡化,省略參數virtual ~Person();...private:std::string name;std::string address; };class Student: public Person { public:Student(); // 再次省略參數~Student();...private:std::string schoolName;std::string schoolAddress; };現在,考慮以下代碼,在此我們調用函數validateStudent,它得到一個Student實參(以傳值方式),并返回它是否有效:
bool validateStudent(Student s); // 函數以by value方式接受Student Student plato; bool platoIsOK = validateStudent(plato); //call the function

????? 很明顯,Student的拷貝構造函數被調用,用plato來初始化參數s。同樣明顯的是,當 validateStudent返回時,s就會被銷毀。所以這個函數的參數傳遞代價是一次Student的拷貝構造函數的調用和一次Student的析構函數的調用。

??????? 但這還不是全部。Student對象內部包含兩個string對象,Student對象還要從一個 Person對象繼承,Person對象內部又包含兩個額外的string對象。最終,以傳值方式傳遞一個Student對象的后果就是引起一次Student的拷貝構造函數的調用,一次Person的拷貝構造函數的調用,以及四次string的拷貝構造函數調用。當Student對象的拷貝被銷毀時,每一個構造函數的調用都對應一個析構函數的調用,所以以傳值方式傳遞一個Student的全部代價是六個構造函數和六個析構函數

這是正確和值得的行為。畢竟,你希望全部對象都得到可靠的初始化和銷毀。盡管如此,pass by reference-to-const方式會更好

bool validateStudent(const Student& s);

???????? 這樣做非常有效:沒有任何構造函數和析構函數被調用,因為沒有新的對象被構造。修改后參數聲明中的const是非常重要的,原先validateStudent以by-value方式接受一個Student參數,所以調用者知道函數絕不會對它們傳入的Student做任何改變,validateStudent只能改變它的復件。現在Student以引用方式傳遞,同時將它聲明為const是必要的,否則調用者必然擔心validateStudent改變了它們傳入的Student。

???????? 以傳引用方式傳遞參數還可以避免切斷問題(slicing problem)。當一個派生類對象作為一個基類對象被傳遞(傳值方式),基類的拷貝構造函數被調用,而那些使得對象行為像一個派生類對象的特化性質被“切斷”了,只剩下一個純粹的基類對象例如,假設你在一組實現一個圖形窗口系統的類上工作:

class Window { public:...std::string name() const; // 返回窗口名稱virtual void display() const; // 顯示窗口及其內容 };class WindowWithScrollBars: public Window { public:...virtual void display() const; };
??????? 所有Window對象都有一個名字(name函數),而且所有的窗口都可以顯示(display函數)。display為 virtual的事實清楚地告訴你:基類的Window對象的顯示方法有可能不同于專門的WindowWithScrollBars對象的顯示方法?,F在,假設你想寫一個函數打印出一個窗口的名字,并隨后顯示這個窗口。以下是 錯誤示范

void printNameAndDisplay(Window w) //incorrect! 參數可能被切割 {std::cout << w.name();w.display(); }考慮當你用一個 WindowWithScrollBars 對象調用這個函數時會發生什么:
WindowWithScrollBars wwsb; printNameAndDisplay(wwsb);??????? 參數w將被作為一個Window對象構造——它是被傳值的,而且使wwsb表現得像一個 WindowWithScrollBars對象的特殊信息都被切斷了。在printNameAndDisplay中,全然不顧傳遞給函數的那個對象的類型,w將始終表現得像一個Window 類的對象(因為其類型是Window)。因此在printNameAndDisplay中調用display將總是調用 Window::display,絕不會是WindowWithScrollBars::display。繞過切斷問題的方法就是以passby reference-to-const方式傳遞w:
void printNameAndDisplay(const Window& w) { // 參數不會被切割std::cout << w.name();w.display(); }

???????? 現在傳進來的窗口是什么類型,w就表現出那種類型。用指針實現引用是非常典型的做法,所以pass by reference實際上通常意味著傳遞一個指針。由此可以得出結論,如果你有一個內置類型對象(一個int),以傳值方式傳遞它常常比傳引用方式更高效;同樣的建議也適用于 STL 中的迭代器和函數對象。

一個對象小,并不意味著調用它的拷貝構造函數就是廉價的。很多對象(包括大多數STL容器)內含的東西只比一個指針多一些,但是拷貝這樣的對象必須同時拷貝它們指向的每一樣東西,那將非常昂貴。即使當小對象有一個廉價的拷貝構造函數,也會存在性能問題。一些編譯器對內置類型和用戶自定義類型并不一視同仁,即使他們有同樣的底層表示。例如,一些編譯器拒絕將僅由一個double組成的對象放入一個寄存器中,即使通常它們非常愿意將一個純粹的double 放入那里。當這種事發生,你以傳引用方式傳遞這樣的對象更好一些,因為編譯器理所當然會將一個指針(引用的實現)放入寄存器。

小的用戶定義類型不一定是傳值的上等候選者的另一個原因是:作為用戶定義類型,它的大小常常變化。通常情況下,你能合理地假設傳值廉價的類型僅有內置類型及STL中的迭代器和函數對象。對其他任何類型,請盡量以pass-by-reference-to-const替換pass-by-value。


總結

以上是生活随笔為你收集整理的条款20 宁以pass-by-reference-to-const替换pass-by-value的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。