[心得] 關於設計class的衡量方式
class如何設計,這個題目太大了
沒辦法只用一篇文章交待完畢,下面只能提我想到的
(1)class vs struct vs union
union用途比較特殊,不再特別提
traits、metafunction、functor,使用struct
有不變式者使用class,否則用struct
補充:
這裡說明一下如何判斷有無不變式
當data成員可以各自任意獨立變更內容時,表示沒有不變式
例如一個類別有月、日欄位,哪些日合法要看月的值,有不變式
或是有一欄位表示元素數量,一欄位指向各元素存放位置,也有不變式
(2)3/5/0法則
除非必要,否則不要自己寫copy、move、dtor
如果寫了其中一個,通常也要寫其他四個成員函數
(3)obj成員 vs ptr成員 vs ref成員
先判斷採組合還是聚合,再判斷是否有降低類型相依性的需要
smart ptr,有所有權
raw ptr,盡量讓其沒有所有權(見補充)
ref,沒有所有權,而且不能改變值
盡量用std::function取代函數指標
補充:
C++ Core Guidelines R.3與R.4,建議讓raw ptr與ref沒有所有權
但raw ptr有例外,例如舊代碼,或是底層實作的需要
(4)public權限 vs private權限 vs protected權限
data成員:
各成員之間有不變式關係,private權限
各成員之間無不變式關係,public權限
避免protected data
non-const data權限要一樣
成員函數:
提供給任意使用者的介面,public權限
僅提供給子類的介面,protected權限
只提供類別內部使用,private權限
(5)public繼承 vs private繼承 vs protected繼承
有"IS-A"或"-ABLE"關係,繼承介面(可能還有實作),public繼承
有"HAS-A"關係,僅繼承實作,private繼承
補充:
http://www.gotw.ca/publications/mill06.htm
private繼承跟protected繼承可以用在受控制的多型
這用途很罕見,所以protected繼承基本上可以忽略
(6)private繼承 vs 組合
在"HAS-A"關係的前提下考慮這個
優先用組合,除非遇到Effective C++ Item 39說的特殊情況
(7)public繼承 vs 組合
https://isocpp.org/wiki/faq/multiple-inheritance
看上面連結的經驗法則1
主要目的是代碼重用時,組合
主要目的是多型時,繼承
補充:
記住,繼承的耦合度高於組合
繼承的主要目的不是代碼重用,盡量避免實作繼承
(8)virtual函數 vs non-virtual函數
讓子類只繼承介面,pure virtual函數
讓子類繼承介面跟預設實作,impure virtual函數
提供預設實作並強迫子類顯式呼叫,為pure virtual函數提供定義
讓子類繼承介面跟強制實作,non-virtual函數
讓子類只改寫方法的某些步驟,用NVI包裝virtual函數(template pattern)
多型類別必須有virtual dtor
補充:
記得預設參數是靜態綁定,不要試圖改寫
(9)operator overloading
除非必要,否則不要重寫
盡量避免重寫operator&&跟operator||,會失去短路求值特性
盡量避免重寫unary &,遇到incomplete type會導致結果無法預期
operator+以operator+=來實作,以此類推
operator+設計成非成員函數,operator+=設計成成員函數,以此類推
unary設計成成員函數,binary設計成非成員函數
postfix ++以prefix ++實作,以此類推
operator=不要設virtual
(10)ctor
不要自己寫一個只初始data成員的default ctor
單參數ctor(不含copy跟move)宣告成explicit,除非希望隱式轉換
初始過程如果想要有虛函數的行為,請改用factory pattern
考慮是否需要用using繼承父類的ctor
(11)多型類別禁止public copy/move
如果想複製,請改用prototype pattern
(12)exception safety
dtor、swap、move、回收函數必須做到nothrow
default ctor、operator==(含其他比較運算子)盡量做到nothrow
(13)當函數需要直接存取內部成員時才作為成員函數
以下為例外情況:
虛函數
operator overloading另有一套判斷方式
重載集合不是每個函數都會直接存取內部成員
返回this的函數
(14)如果有作為成員函數的需要,再繼續判斷是否改用friend函數
https://isocpp.org/wiki/faq/friends
該物件本身所在的函數參數位置不是第一個(見補充),friend函數
涉及binary運算,friend函數
其餘情況,優先用成員函數
補充:
應該是在講std::invoke
(15)const成員函數 vs non-const成員函數
除非確實需要修改data成員,否則選擇const成員函數
(16)static成員 vs non-static成員
成員基於類別,static成員
成員基於物件,non-static成員
(17)動態多型 vs 靜態多型
優先使用動態多型,除非有用CRTP消除virtual函數開銷的必要
(18)函數的定義是否放在class body
為了降低編譯相依性或不要inline,不放
為了可讀性或inline,放
補充:
virtual函數不會inline,ctor跟dtor盡量不要inline
除非必要,否則不考慮函數是否inline
(19)多重繼承
https://isocpp.org/wiki/faq/multiple-inheritance
考慮bridge pattern跟nested generalization能否作為替代方案
補充:
使用多重繼承需要很多前置知識,謹慎使用
(20)虛擬繼承
虛擬繼承的主要用途是搭配多重繼承
盡量避免在virtual base放data
(21)如果需要限制繼承深度
優先考慮final
限制之後往下最多可以繼承幾層,虛擬繼承 + private ctor
(22)design pattern
視使用情境而定,例如
strategy,動態切換演算法
adapter,解決介面不相容的問題
singleton,確保一個類只有一個實例
(23)friend函數 vs static成員函數 vs non-static成員函數
如果經過(13)跟(14)仍無法決定,可以繼續參考下面
如果無法用ADL找到該函數的聲明,不考慮friend函數
如果需要相當於this的參數,不考慮static成員函數
如果不要相當於this的參數,不考慮non-static成員函數
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.237.69.224 (臺灣)
※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1624780030.A.7B4.html
推
06/28 01:20,
2年前
, 1F
06/28 01:20, 1F
→
06/28 18:45,
2年前
, 2F
06/28 18:45, 2F
→
06/28 18:45,
2年前
, 3F
06/28 18:45, 3F
※ 編輯: loveflames (36.237.69.224 臺灣), 06/28/2021 18:48:25
推
06/28 23:50,
2年前
, 4F
06/28 23:50, 4F
→
06/29 01:13,
2年前
, 5F
06/29 01:13, 5F
→
06/29 01:14,
2年前
, 6F
06/29 01:14, 6F
→
06/29 10:02,
2年前
, 7F
06/29 10:02, 7F
→
06/29 10:04,
2年前
, 8F
06/29 10:04, 8F