Effective C++ 第四章
- 4. Designs and Declarations 设计与声明
- 18: Make interfaces easy to use correctly and hard to use incorrectly.
- 19:Treat class design as type design.
- 20: Prefer pass-by-reference-to-const to pass-by-value.
- 21: Don't try to return a reference when you must return an object.
- 22: Declare data members private.
- 23: Prefer non-member non-friend functions to member functions.
- 24: Declare non-member functions when type conversions should apply to all parameters.
- 25: Consider support for a non-throwing swap.
4. Designs and Declarations 设计与声明
18: Make interfaces easy to use correctly and hard to use incorrectly.
让接口容易被正确使用,不易被误用
促进正确使用:
- 保持接口的一致性,如STL容器都有一个名为
size
的成员函数 - 与内置类型的行为兼容
防止错误使用:
-
建立新类型、限制类型上的操作,如不直接返回指针,而是返回智能指针,避免客户忘记delete
-
束缚对象值
导入 外覆类型(wrapper types)区分年、月、日
Date d(Month::Mar(), Day(30), Year(1995));
19:Treat class design as type design.
设计class犹如设计type
设计class需要考虑的问题:
-
新type的对象如何被创建和销毁(第八章)
构造函数、析构函数、内存分配函数和释放函数
-
对象的初始化和赋值有什么差别(条款4)
构造函数和赋值操作符
-
以值传递如何实现
-
新type的合法值
-
是否需要继承(条款34、36)
-
类型转换(条款15)
-
合法的操作符(条款23、24、46)
-
成员变量、成员函数的访问控制
-
未声明接口(条款29)
-
class or class template
-
是否需要新的type,或者增加非成员函数即可
20: Prefer pass-by-reference-to-const to pass-by-value.
-
传递对象给函数参数,pass-by-value会增加调用构造函数和析构函数的成本,引用传递可以避免
-
const类型,避免对原始对象的修改
-
可以避免切割(slicing)问题:
派生类对象传递给函数参数的基类对象,派生类的特化性质会被切割掉,只剩下基类对象
引用传递可以实现多态,解决切割问题
-
例外:
内置类型、STL的迭代器、函数对象
21: Don't try to return a reference when you must return an object.
必须返回对象时,别妄想返回其reference
- 不要返回指向局部的、堆上的或static的对象的指针/引用
- 直接返回对象,交给编译器优化
有理数乘法的正确实现版本
inline const Rational operator*(const Rational& a, const Rational& b) {
return Rational(a.n*b.n, a.d*b.d);
}
22: Declare data members private.
将成员变量声明为private
pirvate的成员变量,可以
- 划分访问控制
- 实现封装,给客户提供一致性的函数接口
- 声明为protected的变量,修改或取消同样会引发所有继承类的改变
- 封装的角度,只有两种访问权限:private和其他
23: Prefer non-member non-friend functions to member functions.
24: Declare non-member functions when type conversions should apply to all parameters.
若所有的参数都需要类型转换,请为此采用non-member函数
要使下面的代码通过编译:
class Rational {
public:
Rational(int numerator=0, int denominator=1); // 构造函数不为explicit,允许int隐式转换为Rational
int numerator() const;
int denominator() const;
private:
...
};
Rational a(1, 4);
Rational result;
result = a * 2;
result = 2 * a;
有理数乘法应该设计为non-member:
const Rational operator*(const Rational& a, const Rational& b) {
return (a.numerator()*b.numerator(), a.denominator()*b.denominator());
}
可以避免fiend函数就该避免,这里不需要设计成为friend函数
25: Consider support for a non-throwing swap.
考虑写出一个不抛异常的swap函数