[心得] 关于设计class的衡量方式

楼主: loveflames (咕啾咕啾魔法阵)   2021-06-27 15:47:07
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)static成员 vs non-static成员
成员基于类别,static成员
成员基于物件,non-static成员
(5)public权限 vs private权限 vs protected权限
data成员:
各成员之间有不变式关系,private权限
各成员之间无不变式关系,public权限
避免protected data
non-const data权限要一样
成员函数:
提供给任意使用者的接口,public权限
仅提供给子类的接口,protected权限
只提供类别内部使用,private权限
(6)public继承 vs private继承 vs protected继承
有"IS-A"或"-ABLE"关系,继承接口(可能还有实作),public继承
有"HAS-A"关系,仅继承实作,private继承
补充:
http://www.gotw.ca/publications/mill06.htm
private继承跟protected继承可以用在受控制的多型
这用途很罕见,所以protected继承基本上可以忽略
(7)private继承 vs 组合
在"HAS-A"关系的前提下考虑这个
优先用组合,除非以下情况(细节见Effective C++ Item 39)
为了节省empty class占用的空间
需要用到protected成员
需要改写父类的virtual函数,例如template pattern
(8)public继承 vs 组合
https://isocpp.org/wiki/faq/multiple-inheritance
看上面连结的经验法则1
主要目的是代码重用时,组合
主要目的是多型时,继承
补充:
记住,继承的耦合度高于组合
继承的主要目的不是代码重用,尽量避免实作继承
(9)virtual函数 vs non-virtual函数
让子类只继承接口,pure virtual函数
让子类继承接口跟默认实作,impure virtual函数
提供默认实作并强迫子类显式呼叫,为pure virtual函数提供定义
让子类继承接口跟强制实作,non-virtual函数
让子类只改写方法的某些步骤,用NVI包装virtual函数(template pattern)
多型类别必须有virtual dtor
补充:
记得默认参数是静态绑定,不要试图改写
(10)operator overloading
除非必要,否则不要重写
尽量避免重写operator&&跟operator||,会失去短路求值特性
尽量避免重写unary &,遇到incomplete type会导致结果无法预期
operator+以operator+=来实作,以此类推
operator+设计成非成员函数,operator+=设计成成员函数,以此类推
unary设计成成员函数,binary设计成非成员函数
postfix ++以prefix ++实作,以此类推
operator=不要设virtual
如果重写类型转换函数,请宣告成explicit,除非希望隐式转换
(11)ctor
不要自己写一个只初始data成员的default ctor
单参数ctor(不含copy跟move)宣告成explicit,除非希望隐式转换
初始过程如果想要有虚函数的行为,请改用factory pattern
考虑是否需要用using继承父类的ctor
(12)多型类别禁止public copy/move
如果想复制,请改用prototype pattern
(13)exception safety
dtor、swap、move、回收函数必须做到nothrow
default ctor、operator==(含其他比较运算子)尽量做到nothrow
(14)当函数需要直接存取内部成员时才作为成员函数
以下为例外情况:
虚函数
operator overloading另有一套判断方式
重载集合不是每个函数都会直接存取内部成员
返回this的函数
(15)如果有作为成员函数的需要,再继续判断是否改用friend函数
https://isocpp.org/wiki/faq/friends
第一个函数参数不是该物件本身(见补充),friend函数
涉及binary运算,friend函数
其余情况,优先用成员函数
补充:
应该是在讲std::invoke
(16)friend函数 vs static成员函数 vs non-static成员函数
如果经过(14)跟(15)仍无法决定,可以继续参考下面
如果无法用ADL找到该函数的声明,不考虑friend函数
如果需要相当于this的参数,不考虑static成员函数
如果不要相当于this的参数,不考虑non-static成员函数
补充:
http://www.gotw.ca/publications/mill02.htm
非成员函数如果不满足接口原则,就没有设计成friend的意义
static成员函数如果需要this,就没有不用non-static版本的理由
(17)const成员函数 vs non-const成员函数
除非确实需要修改data成员,否则选择const成员函数
(18)动态多型 vs 静态多型
优先使用动态多型,除非有用CRTP消除virtual函数开销的必要
(19)函数的定义是否放在class body
为了降低编译相依性或不要inline,不放
为了可读性或inline,放
补充:
virtual函数不会inline,ctor跟dtor尽量不要inline
除非必要,否则不考虑函数是否inline
函数定义如果不放在class body,则定义应放在cpp档
(20)多重继承
https://isocpp.org/wiki/faq/multiple-inheritance
考虑bridge pattern跟nested generalization能否作为替代方案
补充:
使用多重继承需要很多前置知识,谨慎使用
(21)虚拟继承
虚拟继承的主要用途是搭配多重继承
尽量避免在virtual base放data
(22)如果需要限制继承深度
优先考虑final
限制之后往下最多可以继承几层,虚拟继承 + private ctor
(23)design pattern
视使用情境而定,例如
strategy,动态切换算法
adapter,解决接口不相容的问题
singleton,确保一个类只有一个实例
(24)mixin
主要目的是代码重用,由任意个元件组成一个新类别,有下面两种方式实现
可变模板 + 多重继承,每个基类皆为一个元件
模板 + 单继承,每层皆为一个元件
补充:
每个元件功能尽可能单一化,且采public继承
public继承一般不是为了代码重用,所以使用mixin前请先了解一般的继承用法
多重继承版本,mixin元件可以共享方法,但要转型才能使用
单继承版本,mixin元件只能单向共享内层的方法,不用转型就能使用
(25)常数
执行期常数,const
编译期常数 + 有使用具名enum的必要,enum class
编译期常数 + 一般情况,constexpr
补充:
enum每个常数之间要有足够的关联,不能只用来包装一堆无关常数
用enum class取代enum,可以防止隐式转型及名称污染
(26)类型成员
nested class:不要同时定义class及声明该类型的变量
enum:不要同时定义enum及声明该类型的变量
类型别名:用using取代typedef,因为前者可读性高又能用在alias template
补充:
data成员、成员函数、类型成员应按性质各自集中摆放
故类型成员不宜跟data成员混在一起
作者: beatssola (Shark5566)   2021-06-28 01:20:00
楼主: loveflames (咕啾咕啾魔法阵)   2021-06-28 18:45:00
再增加一项,因为static成员函数有办法间接存取non-static成员
作者: FY4   2021-06-28 23:50:00
作者: KanzakiHAria (神崎・H・アリア)   2021-06-29 01:13:00
singleton最简单的方式把解构建构private加个return static local自己的get instance函数
楼主: loveflames (咕啾咕啾魔法阵)   2021-06-29 10:02:00
话说policy based design我找不到不改成组合的理由而组合没办法取代mixin的横向展延性质
作者: unmolk (UJ)   2021-07-02 17:26:00
感谢分享!获益良多
作者: shibin (喜饼)   2021-07-02 19:09:00
楼主: loveflames (咕啾咕啾魔法阵)   2021-07-06 12:11:00
后来补上几个新项目
作者: hhashoww (人生处处充满惊奇)   2021-07-24 18:41:00
新手看不懂 但还是推! 感谢整理
楼主: loveflames (咕啾咕啾魔法阵)   2021-07-25 00:21:00
语法看到一定程度应该就看得懂了,这篇主要是提供选择的方式

Links booklink

Contact Us: admin [ a t ] ucptt.com