effective c++(二)

當爲派生類撰寫複製構造函數時,必須很小心地也複製其基類成分,讓派生類的複製構造函數調用相應的基類函數

class PriorityCustomer:public Customer
{
 public:
 PriorityCustomer(const PriorityCustomer &rhs);
 PriorityCustomer& operator=(const PriorityCusstomer& rhs);
 ...
}
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:Customer(rhs),
 priority(rhs.priority)
{
 ...
}
PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
 Customer::operator=(rhs);
 ...
}

令copy assignment操作符調用複製構造函數是不合理的,因爲這就像試圖構造一個已經存在的對象。同樣讓複製構造函數調用copy assignment操作符同樣無意義,構造函數用來初始化新對象,而assignment操作符只施行與已初始化對象身上,對一個尚未構造好的對象賦值,就像在一個尚未初始化的對象身上做只對已初始化對象纔有意義的事一樣。

所謂資源就是,一旦用了它,將來必須還給系統,把資源放進對象內,便可依賴C++的析構函數自動調用機制確保資源被釋放。記住資源取得時機便是初始化時機

auto_ptr是個類指針對象,也是所謂的智能指針,其析構函數自動對其所指對象調用delete,由於auto_ptr被銷燬時自動刪除它所指之物,所以一定要注意別讓多個auto_ptr指向同一個對象,同時它有一個不尋常的性質,若通過複製構造函數或copy assignment操作符複製它們,它們會變成null,而複製所得的指針將取得資源的唯一擁有權。

auto_ptr和trl::shared_ptr兩者都有其析構函數內做delete而不是delete[]動作,因此對於用動態創建的數組讓它們指向是錯誤的

trl::shared_ptr允許指定所謂的刪除器,那是一個函數或函數對象,當引用次數爲0時便被調用,刪除器對於shared_ptr的構造函數而言是可有可無的第二參數。

複製資源管理對象時,進行的是深度拷貝,對於資源管理對象的複製行爲通常爲抑制複製和施行引用計數法(shared_ptr)

shared_ptr和auto_ptr都提供一個get成員函數,用來執行顯式轉換,也就是它會返回智能指針內部的原始指針(的復件)

std::trl::shared_ptr<Investment>pInv(createInvestment());
int daysHeld(const Investment* pi);
int days=daysHeld(pInv.get());//將pInv內的原始指針傳給daysHeld

就像幾乎所有智能指針一樣,shard_ptr和auto_ptr也重載了->和*,它們允許隱式轉換至底部原始指針,就是可以通過這兩個操作符取訪問類內部資源。

是否提供一個顯式轉換函數還是一個隱式轉換函數主要取決於類被設計執行的特定工作,以及它的使用情況。

當你對這一個指針使用delete,唯一能夠讓delete知道內存中是否存在一個數組大小記錄的方法是由你來告訴它。所以當對一個單一對象使用delete[]或對一個數組使用delete,都會產生未定義的行爲!儘量不要對數組形式做typedefs動作!容易出錯!

shared_ptr構造函數需要一個原始指針,但該構造函數是個explicit構造函數,無法進行隱式轉換,所以如下無法通過編譯

void processWidget(std::trl::shared_otr<Widget> pw,int priority);
processWidget(new Widget,priority());//無法通過編譯

這樣就可以

processWidget(std::trl::shared_ptr<Widget>(new Widget),priority());

編譯器產出一個processWidget調用碼之前,必須首先覈算即將被傳遞的各個實參,因此編譯器必須創建代碼,做下面三件事,調用priority,執行new Widget,調用trl::shared_ptr構造函數,但是這三個事情的執行順序是不確定的,隨機的,由編譯器決定的,因此萬一priority調用異常導致new Widget返回的指針將遺失,因爲在資源被創建和資源被轉換爲資源管理對象兩個時間點之間有可能發生異常干擾。因此要以獨立語句將New對象存儲於智能指針內。因此應該:

std::trl::shared_ptr<Widget>pw(new Widget)
processWidget(pw,priority());

任何接口如果要求客服必須記得做某些事情,就是有着不正確使用的傾向

shared_ptr構造函數堅持其第一參數必須是個指針。通過轉換的也可以接受

讓接口容易被正確使用,不易被誤用,促進正確使用的辦法包括接口的一致性,以及與內置類型的行爲兼容,阻止誤用的辦法包括建立新類型,限制類型上的操作,束縛對象值,以及消除客戶的資源管理責任。

設計高效的類必須要求面對一下的提問:

  • 新類型的對象應該如何被創建和銷燬-影響到你的類的構造函數和析構函數以及內存分配函數和釋放函數
  • 對象的初始化和對象的賦值該有聲明樣的差別-決定你的構造函數和賦值操作符的行爲,以及其間的差異
  • 新的類型的對象如果被passed by value,意味着什麼,copy函數用來定義一個類的pass-by-value該如何實現
  • 什麼是新類型的合法值-決定了錯誤檢查工作,以及函數拋出的異常等等
  • 你的新類型需要配合某個繼承圖系-注意虛函數的影響
  • 新類型需要什麼樣的轉換-爲適當的轉換提供轉換函數
  • 什麼樣的操作符和函數對此新類型而言是合理的-決定聲明哪些函數,哪些爲成員函數
  • 什麼樣的標準函數應該駁回-那些正是你必須聲明爲私有者
  • 誰該取用新類型的成員-決定成員變量哪個爲公有哪個爲私有,決定哪些類型或函數是朋友
  • 什麼是新的類型的未聲明接口-對效率,異常安全性以及資源運用提供何種保證新的類有那麼一般化-這種情況決定是否是應該定義一個新的class template
  • 你真的需要一個新類型嗎-如果只是定義新的派生類以便爲既有的類添加技能,那麼說不定單純定義一或多個函數更好

以pass by reference的傳遞方式效率高得多,沒有任何構造函數或析構函數被調用,因爲沒有任何新對象被創建。

pass by value容易造成參數切割,考慮如下代碼,參悟W會被構造成爲一個window對象,它是by value,所有windowWithScropBars對象的所有特化信息都會被切除,在函數內不論傳遞過來的對象原本是什麼類型,參數W就像一個WINDOW對象,因此在函數內調用的虛函數也都是window的函數

class Window{
 ...
}
class WindowWithScrollBars:public Window{
...
}
void printNameAndDisplay(Window w)
{
 ...
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

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

void printNameAndDisplay(const Window& w)
{
 ...
}

如果你有個獨享屬於內置類型,pass by value往往比pass by reference的效率高些,對於內置類型和stl的迭代器和函數對象,選擇爲pass by value是值得的

任何時候看到一個reference聲明式,你都應該立刻問自己,它的另一個名稱是什麼,因爲它一定是某物的另一個名稱。必須返回對象時,千萬別妄想返回其reference,那一定是糟糕的錯誤!

其實只有兩種訪問權限,private(提供封裝)和其他(不提供封裝),保護成員並不比公有成員更具有封裝性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章