C++ 被沉沒(méi)于接口中。函數(shù)接口、類(lèi)接口、模板接口。每一個(gè)接口都意味著客戶(hù)的代碼和你的代碼互相影響。假設(shè)你在和通情達(dá)理的人打交道,那些客戶(hù)也想做好工作。他們想要正確使用你的接口。在這種情況下,假如他們犯了一個(gè)錯(cuò)誤,就說(shuō)明你的接口至少有部分是不完善的。在理想情況下,假如一個(gè)接口的一種嘗試的用法不符合客戶(hù)的預(yù)期,代碼將無(wú)法編譯,反過(guò)來(lái),假如代碼可以編譯,那么它做的就是客戶(hù)想要的。
class Date { public: Date(int month, int day, int year); ... }; 匆匆一看,這個(gè)接口似乎是合乎情理的(至少在美國(guó)),但是客戶(hù)可能很輕易地造成兩種錯(cuò)誤。首先,他們可能會(huì)以錯(cuò)誤的順序傳遞參數(shù):
Date d(30, 3, 1995); // Oops! Should be "3, 30" , not "30, 3" 第二,他們可能傳遞一個(gè)非法的代表月或日的數(shù)字:
Date d(2, 20, 1995); // Oops! Should be "3, 30" , not "2, 20" ?。ê竺孢@個(gè)例子看上去似乎沒(méi)什么,但是想想鍵盤(pán)上,2 就在 3 的旁邊,這種 "off by one" 類(lèi)型的錯(cuò)誤并不罕見(jiàn)。)
很多客戶(hù)錯(cuò)誤都可以通過(guò)引入新的類(lèi)型來(lái)預(yù)防。確實(shí),類(lèi)型系統(tǒng)是你阻止那些不合適的代碼通過(guò)編譯的主要支持者。在當(dāng)前情況下,我們可以引入簡(jiǎn)單的包裝類(lèi)型來(lái)區(qū)別日,月和年,并將這些類(lèi)型用于 Data 的構(gòu)造函數(shù)。
strUCt Day { struct Month { struct Year { eXPlicit Day(int d) explicit Month(int m) explicit Year(int y) :val(d) {} :val(m) {} :val(y){}
int val; int val; int val; }; }; };
class Date { public: Date(const Month& m, const Day& d, const Year& y); ... }; Date d(30, 3, 1995); // error! wrong types
Date d(Day(30), Month(3), Year(1995)); // error! wrong types
Date d(Month(3), Day(30), Year(1995)); // okay, types are correct 將日,月和年做成封裝數(shù)據(jù)的羽翼豐滿(mǎn)的類(lèi)比上面的簡(jiǎn)單地使用 struct 更好,但是即使是 struct 也足夠證實(shí)明智地引入新類(lèi)型在阻止接口的錯(cuò)誤使用方面能工作得非常出色。
if (a * b = c) ... // oops, meant to do a comparison! 實(shí)際上,這僅僅是另一條使類(lèi)型易于正確使用而難以錯(cuò)誤使用的普遍方針的一種表現(xiàn):除非你有很棒的理由,否則就讓你的類(lèi)型的行為與內(nèi)建類(lèi)型保持一致??蛻?hù)已經(jīng)知道像 int 這樣的類(lèi)型如何表現(xiàn),所以你應(yīng)該努力使你的類(lèi)型的表現(xiàn)無(wú)論何時(shí)都同樣合理。例如,假如 a 和 b 是 int,給 a*b 賦值是非法的。所以除非有一個(gè)非常棒理由脫離這種表現(xiàn),否則,對(duì)你的類(lèi)型來(lái)說(shuō)這樣做也應(yīng)該是非法的。
std::tr1::shared_ptr<Investment> // attempt to create a null pInv(0, getRidOfInvestment); // shared_ptr with a custom deleter; // this won’t compile 唉,這不是合法的 C++。tr1::shared_ptr 的構(gòu)造函數(shù)果斷要求它的第一個(gè)參數(shù)應(yīng)該是一個(gè)指針,而 0 不是一個(gè)指針,它是一個(gè) int。當(dāng)然,它能轉(zhuǎn)型為一個(gè)指針,但那在當(dāng)前情況下并不夠好用,tr1::shared_ptr 果斷要求一個(gè)真正的指針。用強(qiáng)制轉(zhuǎn)型解決這個(gè)問(wèn)題:
std::tr1::shared_ptr<Investment> // create a null shared_ptr with pInv(static_cast<Investment*>(0), // getRidOfInvestment as its getRidOfInvestment); // deleter; see Item 27 for info on // static_cast 據(jù)此,實(shí)現(xiàn)返回一個(gè)以 getRidOfInvestment 作為 deleter 的 tr1::shared_ptr 的 createInvestment 的代碼看起來(lái)就像這個(gè)樣子: