实现原则
“面向对象编程”的关键点是“抽象”和“封装”,而“继承”、“多态”并不是核心,只能算是附加品。所以,我建议你在设计类的时候尽量少用继承和虚函数。如果非要用继承不可,那么我觉得一定要控制继承的层次,用 UML 画个类体系的示意图来辅助检查。如果继承深度超过三层,就说明有点“过度设计”了,需要考虑用组合关系替代继承关系,或者改用模板和泛型。
在设计类接口的时候,我们也要让类尽量简单、“短小精悍”,只负责单一的功能。如果很多功能混在了一起,出现了“万能类”“意大利面条类”(有时候也叫 God Class),就要应用设计模式、重构等知识,把大类拆分成多个各负其责的小类。
常用技巧
- 构造函数委托代理
即存在多个参数不同的构造函数时,如果之间有通用的部分,可直接调用其他构造函数。例如:
class DemoDelegating final{
private:
int a; // 成员变量
public:
DemoDelegating(int x) : a(x) // 基本的构造函数
{}
DemoDelegating() : // 无参数的构造函数
DemoDelegating(0) // 给出默认值,委托给第一个构造函数
{}
DemoDelegating(const string& s) : // 字符串参数构造函数
DemoDelegating(stoi(s)) // 转换成整数,再委托给第一个构造函数
{}
};
- 成员变量初始化
如果你的类有很多成员变量,那么在写构造函数的时候就比较麻烦,必须写出一长串的名字来逐个初始化,不仅麻烦还容易遗漏。在 C++11 里,你可以在类里声明变量的同时给它赋值,实现初始化,这样不但简单清晰,也消除了隐患。
class DemoInit final // 有很多成员变量的类{
private:
int a = 0; // 整数成员,赋值初始化
string s = "hello"; // 字符串成员,赋值初始化
vector v{1, 2, 3}; // 容器成员,使用花括号的初始化列表
public:
DemoInit() = default; // 默认构造函数
~DemoInit() = default; // 默认析构函数
DemoInit(int x) : a(x)
{} // 可以单独初始化成员,其他用默认值
};
- 类型别名
C++11 扩展了关键字 using 的用法,增加了 typedef 的能力,可以定义类型别名。它的格式与 typedef 正好相反,别名在左边,原名在右边,是标准的赋值形式,所以易写易读。
using uint_t = unsigned int; // using别名
typedef unsigned int uint_t; // 等价的typedef
网友评论