天天看點

關于 pass-by-reference-to-const 和 pass-by-value

第一次寫部落格,其實算一個讀書筆記吧,我覺得記一下可能能增加我得印象,廢話不說,直接正題。

這次記一個很小的問題, effective c++ 中 的條款20是:甯以pass-by-reference-to-const替換pass-by-value( 甯願按const引用傳遞來替代按值傳遞),按值傳遞就是函數參數都是以實際實參的複件(副本)為初值,而調用端所獲得的是函數傳回值的一個複件,這些複件系由對象的copy構造函數産出,這可能使得pass-bu-value成為昂貴的(費時的)操作,下面是書中的源碼:

<span style="font-size:18px;">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;
};</span>
           

現在考慮如下代碼:

bool validateStudent ( Student s ); 

Student plato;

bool platoIsOK = validateStudent(plato);

這顯然是按值傳遞,它将會以plato為藍本将s初始化,這将會調用Student的copy構造函數,當validateStudent傳回時s将會被銷毀(析構函數),是以按值傳遞的成本是“Student的一次拷貝函數的調用加上一次析構函數的調用”。

除此之外,Student對象還有兩個string對象,是以每次構造一個Student對象就構造了兩個string對象,此外! Student對象繼承自Person對象,是以構造Student對象前又會構造一個Person對象, 而Person對象又有兩個string對象。。。。,是以,總的計算下來,按值傳遞一個Student對象會導緻”六次構造函數和六次析構函數的調用

!“(看起來很多的樣子)。

然而,如果我們使用pass by reference-to-const (const引用): //前面講過,用const修飾參數是一個很好的習慣,是以盡量使用const

bool validateStudent (const Student& s ) ;

這種傳遞方式效率會高很多,因為沒有任何的構造函數或析構函數會被調用(沒有任何新對象被建立),用const修飾是重要的,這将確定傳入的Student不會被修改。

此外,按引用傳遞還有一個好處,就是能避免slicing(對象切割)問題,(什麼鬼), 當一個子類class對象以by value方式傳遞并被視為一個父類對象,父類的copy構造函數會被調用,而”造成此對象行為像個子類對象“的哪些特化性質全被切割掉了,僅僅留下I一個父類對象”,現在看下面代碼

<span style="font-size:18px;">class Window {
public:
  ...
  std::string name() const;  //傳回視窗名稱
  virtual void display() const; //顯示視窗及其内容
}

class WindowWithScrollBare: public window {
 public:
   ...
   virtual void display() const;
};</span>
           

假設你希望寫個函數列印視窗名稱,然後顯示該視窗,下面是錯誤的示範

<span style="font-size:18px;">void printNameAndDisplay( Window w )   //不正确,參數可能被切割
{
  std::cout<<w.name();
  w.display();
}</span>
           

當我們給它傳遞一個WindowWithScrollBare對象時,

WindowWithScrollBare wwsb;

printNameAndDisplay(wwsb);

參數w會被構造成Window對象而不是WindowWithScrollBare對象,而wwsb的所有特化資訊會被全部切除,在printNameAndDisplay函數内不論傳遞過來的對象是什麼類型,

參數w就像一個window對象(因為他的類型是window)。 是以在printNameAndDisplay内調用display調用的總是Window::display,絕不會是WindowWithScrollBare.Display.

解決切割(slicing)的辦法就是pass by reference-to-const

<span style="font-size:18px;">void printNameAndDisplay( const Window& w )   //不正确,參數可能被切割
{
  std::cout<<w.name();
  w.display();
}</span>
           

這樣,傳進來的視窗是什麼類型,w就表現出那種類型

請記住:

1.盡量以pass-by-reference-to-const替換pass-by-value。前者通常比較高效,并且可避免切割問題。

2.以上規則并不适用于内置類型,以及STL的疊代器和函數對象。對它們而言,pass-by-value往往比較合适。

繼續閱讀