程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Effective C++:條款20:寧以 pass-by-reference-to-const替換pass-by-value

Effective C++:條款20:寧以 pass-by-reference-to-const替換pass-by-value

編輯:C++入門知識

(一)

調用函數的時候如果傳遞參數pass-by-value,那麼函數參數都是以實際實參的副本為初值,調用端所獲得的亦是函數返回值的一個復件。

看下面代碼:

class Person { 
public: 
    Person(); 
    virtual ~Person();  
private: 
    string name; 
    string address; 
};

class Student : public Person { 
public: 
    Student(); 
    ~Student(); 
private: 
    string schoolName; 
    string schoolAddress; 
};

bool validateStudent(Student s); 
Student plato; 
bool platoIsOK = validateStudent(plato);

當上述函數調用validateStudent(plato);時,Student的copy構造函數被調用,以plato為藍本將s初始化。當validateStudent返回s被銷毀。所以參數的傳遞成本是“一次Student copy構造函數調用,一次Student析構函數調用”。

但是還不是整個故事,Student對象內有兩個string對象,Student對象繼承自Person對象,因此必須構造出Person對象,Person對象又有兩個string在其中。

因此以by-value方式傳遞一個Student對象會導致調用一次Student copy構造函數、一次Person copy構造函數、四次string copy構造函數。當那個Student復件被銷毀,每個構造函數動作多需要一個對應的析構函數調用動作。所以總體成本是“六次構造函數和六次析構函數”!

解決方法: pass-by-reference-to-const

bool validateStudent(const Student& s);
這種方式沒有任何構造和析構函數被調用,因為沒有任何新對象被創建。const是必要的,調用者不用擔心validateStudent改變他們傳入的那個Student。


(二)

by reference傳遞參數還可以避免slicing(對象切割)問題。當一個derived class對象以by value傳遞給一個base class對象。base class的copy構造函數被調用,而“構造此對象的行為像個derived class對象”的那些特化性質被切割掉了,僅留下一個base class對象。因為正是base class的構造函數創建了它。

假設有下面的繼承關系:

class Window { 
public: 
    string name() const; 
    virtual void display() const; 
};
class WindowWithScrollBars : public Window { 
public: 
    virtual void display() const; 
};
錯誤的函數寫法:

void printNameAndDisplay(Window w) 
{ 
    std::cout << w.name(); 
    w.display(); 
}
當調用上面這個函數的時候:

WindowWithScrollBars wwsb; 
printNameAndDisplay(wwsb);
w會被構造成一個Window對象:他是pass-by-value,造成wwsb“之所以是個WindowWithScrollBars對象”的所有特化信息被切除。所以display調用的總是Window::display()。這肯定不是我們想要的!!!

解決方法:
以by reference to const的方式傳遞w:

void printNameAndDisplay(const Window& w) { 
    cout << w.name(); 
    w.display(); 
}
現在,傳進來的窗口是什麼類型,w就表現出那種類型。


(三)
某些編譯器對待“內置類型”和“用戶自定義類型”截然不同,縱使兩者有相同的底層表述。有些編譯器會把一個double變量放進緩存器,但是不會把只含有一個double的class放進緩存器。

窺視c++編譯器的底層,你會發現,reference往往以指針實現出來,因此pass-by-reference通常意味著傳遞的是指針。因此如果你有個對象屬於內置類型(例如int)pass by value往往比pass by reference的效率要高些。這個忠告也適用於stl的迭代器和函數對象,因此它們習慣上被設計為pass by value。實踐者有責任看看它們是否高效且不受切割問題的影響。


請記住:
(1)盡量以pass-by-reference-to-const替換pass-by-value.前者通常比較高效,並可避免切割問題(slicing problem).;
(2)以上規則並不適合與內置類型,以及STL的迭代器和函數對象。對它們而言,pass-by-value往往比較適合.。





  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved