條款18:讓接口容易被正確使用淳玩,不易被誤用
- 建立新的類型作為函數(shù)可以防止用戶輸入錯誤的數(shù)據(jù)二汛,考察下面的類構(gòu)造函數(shù):
struct Date {
Date(int day, int month, int year);
};
當(dāng)用戶調(diào)用構(gòu)造函數(shù)時侨糟,很容易輸入錯誤怠惶,比如將day的值當(dāng)成year的值耀态。這時候可以再創(chuàng)建Day轮傍,Month和Year新的數(shù)據(jù)類型:
struct Day {
Day(int d) : val(d) {}
int val;
};
struct Month {
Month(int m) : val(m) {}
int val;
};
struct Year {
Year(int y) : val(y) {}
int val;
};
// Date構(gòu)造函數(shù)以Day,Month首装,Year作為輸入
struct Date {
Date(Month m, Day d, Year y);
};
- 使用共享指針(shared pointer)作為函數(shù)返回值创夜,不僅可以避免用戶忘記delete指針造成的內(nèi)存泄露問題,還可以防止跨DLL delete問題仙逻。
條款19:設(shè)計class猶如設(shè)計type
設(shè)計一個class需要考慮以下問題:
- 如何創(chuàng)建和銷毀對象
- 構(gòu)造函數(shù)與賦值運(yùn)算操作是否有區(qū)別
- 如果對象被值傳遞驰吓,涉及到哪些操作
- type的合法值
- 是否有繼承關(guān)系
- 需要什么樣的轉(zhuǎn)換
- 什么樣的操作符和函數(shù)對此新的type是合理的
- delete掉哪些操作
- 成員哪些為public涧尿,哪些為private
- type的泛化性,是否定義一個具體的class還是一個class template
- 是否真的需要定義一個新的type檬贰,是否可以通過已有type組合達(dá)到目的姑廉。
條款20:寧以pass-by-reference-to-const替換pass-by-value
- 以引用方式傳遞參數(shù)通常比值傳遞方式更加高效,而且還可以避免對象切割問題偎蘸。
- 內(nèi)置類型一般以值傳遞比較合理庄蹋,不適用此規(guī)則。
條款21:必須返回對象時迷雪,別妄想返回其reference
- 如果返回的是一個函數(shù)內(nèi)部的局部對象限书,則切勿以引用方式返回,因為函數(shù)返回后章咧,該局部對象將會被析構(gòu)倦西。
條款22:將成員變量聲明為private
- 將成員變量設(shè)置為private能夠提供更好的封裝性。
- protected變量并不比public更具封裝性赁严。
條款23:寧以Non-member扰柠、Non-friend函數(shù)替換member函數(shù)
- 考察一個類中的成員函數(shù),如果它的實現(xiàn)中不是必須要用類中的私有成員變量疼约,則建議將此函數(shù)不要作為類的成員函數(shù)卤档,而是以獨(dú)立的函數(shù)形式呈現(xiàn),函數(shù)的參數(shù)輸入即是該類的對象程剥。
條款24:若所有參數(shù)皆需類型轉(zhuǎn)換劝枣,請為此采用non-member函數(shù)。
- 數(shù)據(jù)舉了一個復(fù)數(shù)相乘的例子:
class R {
public:
const R operator*(const R& rhs) const;
};
R r1;
R r2 = r1 * 2; // 編譯OK
R r3 = 2 * r1; // 此時編譯不通過
編譯不通過的語句中织鲸,“2”如果在*號前面則不能進(jìn)行隱式轉(zhuǎn)換為一個R對象舔腾。
這時候,采用一個non-member的操作符就比較合適了搂擦。
const R operator*(const R& lhs, const R& rhs)
{
...
}
條款25:考慮寫一個不拋異常的swap函數(shù)
- 在std空間內(nèi)有一個默認(rèn)的swap函數(shù)模板稳诚,它的定義如下:
namespace std {
template <typename T>
void swap(T& a, T& b)
{
T tmp(a);
a = b;
b = tmp;
}
}
這里需要T類型對象支持operator = 操作。
考察以下類瀑踢,如果我們要對其兩個對象進(jìn)行swap操作扳还,將會出現(xiàn)一點效率的問題:
class MyImpl {
public:
private:
std::vector<int> myVec;
};
class MyClass {
public:
MyClass& operator=(MyClass& rhs)
{
...
*(pImpl) = *(rhs.pImpl);
...
}
private:
MyImpl *pImpl;
};
MyClass obj1, obj2;
std::swap(obj1, obj2); // 產(chǎn)生額外的拷貝操作,效率非常低
如果我們此時調(diào)用std::swap函數(shù)對MyClass對象進(jìn)行交換操作時橱夭,則會導(dǎo)致數(shù)據(jù)的拷貝普办,并且并沒有達(dá)到交換的目的。
此時需要在MyClass類中提供一個swap成員函數(shù)徘钥,實現(xiàn)真正的交換功能:
class MyClass {
public:
void swap(const MyClass& rhs)
{
std::swap(pImpl, rhs.pImpl);
}
};
接下來就需要對std空間內(nèi)的swap函數(shù)針對MyClass類進(jìn)行特例化:
namespace std {
template <> // 這里為空代表是對swap模板進(jìn)行特例化
void swap(MyClass& a, MyClass& b) noexcept
{
a.swap(b);
}
}
int main()
{
MyClass obj1, obj2;
std::swap(obj1, obj2); // 調(diào)用上述特例化的版本,并且調(diào)用MyClass類中的swap成員函數(shù)肢娘,達(dá)到真正交換的目的呈础。
return 0;
}