在 C++ 中,就像其它面向?qū)ο缶幊陶Z(yǔ)言,可以通過定義一個(gè)新的類來(lái)定義一個(gè)新的類型。作為一個(gè) C++ 開發(fā)者,你的大量時(shí)間就這樣花費(fèi)在增大你的類型系統(tǒng)。這意味著你不僅僅是一個(gè)類的設(shè)計(jì)者,而且是一個(gè)類型的設(shè)計(jì)者。重載函數(shù)和運(yùn)算符,控制內(nèi)存分配和回收,定義對(duì)象的初始化和終結(jié)過程——這些全在你的掌控之中。因此你應(yīng)該在類設(shè)計(jì)中傾注大量心血,接近語(yǔ)言設(shè)計(jì)者在語(yǔ)言內(nèi)建類型的設(shè)計(jì)中所傾注的大量心血。
設(shè)計(jì)良好的類是有挑戰(zhàn)性的,因?yàn)樵O(shè)計(jì)良好的類型是有挑戰(zhàn)性的。良好的類型擁有簡(jiǎn)單自然的語(yǔ)法,符合直覺的語(yǔ)義,以及一個(gè)或更多高效的實(shí)現(xiàn)。在 C++ 中,一個(gè)缺乏計(jì)劃的類設(shè)計(jì),使其不可能達(dá)到上述任何一個(gè)目標(biāo)。甚至一個(gè)類的成員函數(shù)的執(zhí)行特性可能受到它們是被如何聲明的影響。
那么,如何才能設(shè)計(jì)高效的類呢?首先,你必須理解你所面對(duì)的問題。實(shí)際上每一個(gè)類都需要你面對(duì)下面這些問題,其答案通常就導(dǎo)向你的設(shè)計(jì)的限制因素:
你的新類型的對(duì)象應(yīng)該如何創(chuàng)建和銷毀?如何做這些將影響到你的類的構(gòu)造函數(shù)和析構(gòu)函數(shù),以及內(nèi)存分配和回收的函數(shù)(operator new,operator new[],operator delete,和 operator delete[])的設(shè)計(jì),除非你不寫它們。
對(duì)象的初始化和對(duì)象的賦值應(yīng)該有什么不同?這個(gè)問題的答案決定了你的構(gòu)造函數(shù)和你的賦值運(yùn)算符的行為和它們之間的不同。這對(duì)于不混淆初始化和賦值是很重要的,因?yàn)樗鼈兿喈?dāng)于不同的函數(shù)調(diào)用。
以值傳遞(passed by value)對(duì)于你的新類型的對(duì)象意味著什么?記住,拷貝構(gòu)造函數(shù)定義了一個(gè)新類型的傳值(pass-by-value)如何實(shí)現(xiàn)。
你的新類型的合法值的限定條件是什么?通常,對(duì)于一個(gè)類的數(shù)據(jù)成員來(lái)說,僅有某些值的組合是合法的。那些組合決定了你的類必須維持的不變量。這些不變量決定了你必須在成員函數(shù)內(nèi)部進(jìn)行錯(cuò)誤檢查,特別是你的構(gòu)造函數(shù),賦值運(yùn)算符,以及 "setter" 函數(shù)。它可能也會(huì)影響你的函數(shù)拋出的異常,以及你的函數(shù)的異常規(guī)范(exception specification)(你用到它的可能性很?。?。
你的新類型是否適合放進(jìn)一個(gè)繼承圖表中?如果你從已經(jīng)存在的類繼承,你將被那些類的設(shè)計(jì)所約束,特別是它們的函數(shù)是 virtual 還是 non-virtual。如果你希望允許其他類繼承你的類,將影響到你是否將函數(shù)聲明為 virtual,特別是你的析構(gòu)函數(shù)。
你的新類型允許哪種類型轉(zhuǎn)換?你的類型身處其它類型的海洋中,所以是否要在你的類型和其它類型之間有一些轉(zhuǎn)換?如果你希望允許 T1 類型的對(duì)象隱式轉(zhuǎn)型為 T2 類型的對(duì)象,你就要么在 T1 類中寫一個(gè)類型轉(zhuǎn)換函數(shù)(例如,operator T2),要么在 T2 類中寫一個(gè)非顯式的構(gòu)造函數(shù),而且它們都要能夠以單一參數(shù)調(diào)用。如果你希望僅僅允許顯示轉(zhuǎn)換,你就要寫執(zhí)行這個(gè)轉(zhuǎn)換的函數(shù),而且你還需要避免使它們的類型轉(zhuǎn)換運(yùn)算符或非顯式構(gòu)造函數(shù)能夠以一個(gè)參數(shù)調(diào)用。
對(duì)于新類型哪些運(yùn)算符和函數(shù)有意義?這個(gè)問題的答案決定你應(yīng)該為你的類聲明哪些函數(shù)。其中一些是成員函數(shù),另一些不是。
哪些標(biāo)準(zhǔn)函數(shù)不應(yīng)該被接受?你需要將那些都聲明為 private。
你的新類型中哪些成員可以被訪問?這個(gè)問題的可以幫助你決定哪些成員是 public,哪些是 protected,以及哪些是 private。它也可以幫助你決定哪些類和/或函數(shù)應(yīng)該是友元,以及一個(gè)類嵌套在另一個(gè)類內(nèi)部是否有意義。
什么是你的新類型的 "undeclared interface"?它對(duì)于性能考慮,異常安全(exception safety),以及資源使用(例如,鎖和動(dòng)態(tài)內(nèi)存)提供哪種保證?你在這些領(lǐng)域提供的保證將強(qiáng)制影響你的類的實(shí)現(xiàn)。
你的新類型有多大程度的通用性?也許你并非真的要定義一個(gè)新的類型。也許你要定義一個(gè)整個(gè)的類型家族。如果是這樣,你不需要定義一個(gè)新的類,而是需要定義一個(gè)新的類模板。
一個(gè)新的類型真的是你所需要的嗎?是否你可以僅僅定義一個(gè)新的繼承類,以便讓你可以為一個(gè)已存在的類增加一些功能,也許通過簡(jiǎn)單地定義一個(gè)或更多非成員函數(shù)或模板能更好地達(dá)成你的目標(biāo)。
回答這些問題是困難的,所以定義高效的類是有挑戰(zhàn)性的。既然,在 C++ 中用戶自定義類生成的類型至少可以和內(nèi)建類型一樣好,那就做好它,它會(huì)使一切努力都變的有價(jià)值。
設(shè)計(jì)良好的類是有挑戰(zhàn)性的,因?yàn)樵O(shè)計(jì)良好的類型是有挑戰(zhàn)性的。良好的類型擁有簡(jiǎn)單自然的語(yǔ)法,符合直覺的語(yǔ)義,以及一個(gè)或更多高效的實(shí)現(xiàn)。在 C++ 中,一個(gè)缺乏計(jì)劃的類設(shè)計(jì),使其不可能達(dá)到上述任何一個(gè)目標(biāo)。甚至一個(gè)類的成員函數(shù)的執(zhí)行特性可能受到它們是被如何聲明的影響。
那么,如何才能設(shè)計(jì)高效的類呢?首先,你必須理解你所面對(duì)的問題。實(shí)際上每一個(gè)類都需要你面對(duì)下面這些問題,其答案通常就導(dǎo)向你的設(shè)計(jì)的限制因素:
你的新類型的對(duì)象應(yīng)該如何創(chuàng)建和銷毀?如何做這些將影響到你的類的構(gòu)造函數(shù)和析構(gòu)函數(shù),以及內(nèi)存分配和回收的函數(shù)(operator new,operator new[],operator delete,和 operator delete[])的設(shè)計(jì),除非你不寫它們。
對(duì)象的初始化和對(duì)象的賦值應(yīng)該有什么不同?這個(gè)問題的答案決定了你的構(gòu)造函數(shù)和你的賦值運(yùn)算符的行為和它們之間的不同。這對(duì)于不混淆初始化和賦值是很重要的,因?yàn)樗鼈兿喈?dāng)于不同的函數(shù)調(diào)用。
以值傳遞(passed by value)對(duì)于你的新類型的對(duì)象意味著什么?記住,拷貝構(gòu)造函數(shù)定義了一個(gè)新類型的傳值(pass-by-value)如何實(shí)現(xiàn)。
你的新類型的合法值的限定條件是什么?通常,對(duì)于一個(gè)類的數(shù)據(jù)成員來(lái)說,僅有某些值的組合是合法的。那些組合決定了你的類必須維持的不變量。這些不變量決定了你必須在成員函數(shù)內(nèi)部進(jìn)行錯(cuò)誤檢查,特別是你的構(gòu)造函數(shù),賦值運(yùn)算符,以及 "setter" 函數(shù)。它可能也會(huì)影響你的函數(shù)拋出的異常,以及你的函數(shù)的異常規(guī)范(exception specification)(你用到它的可能性很?。?。
你的新類型是否適合放進(jìn)一個(gè)繼承圖表中?如果你從已經(jīng)存在的類繼承,你將被那些類的設(shè)計(jì)所約束,特別是它們的函數(shù)是 virtual 還是 non-virtual。如果你希望允許其他類繼承你的類,將影響到你是否將函數(shù)聲明為 virtual,特別是你的析構(gòu)函數(shù)。
你的新類型允許哪種類型轉(zhuǎn)換?你的類型身處其它類型的海洋中,所以是否要在你的類型和其它類型之間有一些轉(zhuǎn)換?如果你希望允許 T1 類型的對(duì)象隱式轉(zhuǎn)型為 T2 類型的對(duì)象,你就要么在 T1 類中寫一個(gè)類型轉(zhuǎn)換函數(shù)(例如,operator T2),要么在 T2 類中寫一個(gè)非顯式的構(gòu)造函數(shù),而且它們都要能夠以單一參數(shù)調(diào)用。如果你希望僅僅允許顯示轉(zhuǎn)換,你就要寫執(zhí)行這個(gè)轉(zhuǎn)換的函數(shù),而且你還需要避免使它們的類型轉(zhuǎn)換運(yùn)算符或非顯式構(gòu)造函數(shù)能夠以一個(gè)參數(shù)調(diào)用。
對(duì)于新類型哪些運(yùn)算符和函數(shù)有意義?這個(gè)問題的答案決定你應(yīng)該為你的類聲明哪些函數(shù)。其中一些是成員函數(shù),另一些不是。
哪些標(biāo)準(zhǔn)函數(shù)不應(yīng)該被接受?你需要將那些都聲明為 private。
你的新類型中哪些成員可以被訪問?這個(gè)問題的可以幫助你決定哪些成員是 public,哪些是 protected,以及哪些是 private。它也可以幫助你決定哪些類和/或函數(shù)應(yīng)該是友元,以及一個(gè)類嵌套在另一個(gè)類內(nèi)部是否有意義。
什么是你的新類型的 "undeclared interface"?它對(duì)于性能考慮,異常安全(exception safety),以及資源使用(例如,鎖和動(dòng)態(tài)內(nèi)存)提供哪種保證?你在這些領(lǐng)域提供的保證將強(qiáng)制影響你的類的實(shí)現(xiàn)。
你的新類型有多大程度的通用性?也許你并非真的要定義一個(gè)新的類型。也許你要定義一個(gè)整個(gè)的類型家族。如果是這樣,你不需要定義一個(gè)新的類,而是需要定義一個(gè)新的類模板。
一個(gè)新的類型真的是你所需要的嗎?是否你可以僅僅定義一個(gè)新的繼承類,以便讓你可以為一個(gè)已存在的類增加一些功能,也許通過簡(jiǎn)單地定義一個(gè)或更多非成員函數(shù)或模板能更好地達(dá)成你的目標(biāo)。
回答這些問題是困難的,所以定義高效的類是有挑戰(zhàn)性的。既然,在 C++ 中用戶自定義類生成的類型至少可以和內(nèi)建類型一樣好,那就做好它,它會(huì)使一切努力都變的有價(jià)值。

