《Effective C++》第三版-4. 设计与声明(Design and Declarations)
目次[*]条款17:让接口容易被精确使用,不易被误用(Make interfaces easy to use correctly and hard to use incorrectly)
[*]限制类型和值
[*]规定能做和不能做的事
[*]提供举动一致的接口
[*]条款19:设计class犹如设计type(Treat class design as type design)
[*]条款20:宁以pass-by-reference-to-const更换pass-by-value(Prefer pass-by-reference-to-cons to pass-by-value)
[*]避免构造和析构
[*]避免对象切割
[*]破例
[*]条款21:必须返回对象时,别妄想返回其reference(Don’t try to return a reference when you must return an object)
[*]条款22:成员变量声明为private(Declare data members private)
[*]条款23:宁以non-member、non-friend更换member函数(Prefer non-member non-friend functions to member functions)
[*]条款24:若所有参数皆需类型转换,请为此采用non-member函数(Declare non-member functions when type conversions should apply to all parameters)
[*]条款25:思量写出一个不抛非常的swap函数(Consider support for a non-throwing swap)
[*]缺省的swap
[*]特化的swap
[*]使用swap的总结
条款17:让接口容易被精确使用,不易被误用(Make interfaces easy to use correctly and hard to use incorrectly)
限制类型和值
class Date {
public:
Date(int month, int day, int year);//可能月日年顺序错,可能传递无效的月份或日期
...
};可使用类型系统(type system)规避以上错误,即引入外覆类型(wrapper type)区别年代日:
struct Day {
explicite Day(int d)
: val(d) { }
int val;
}
struct Month {
explicite Month(int m)
: val(m) { }
int val;
}
struct Year{
explicite Year(int y)
: val(y) { }
int val;
}
class Date {
public:
Date(const Month& m, const Day& d, const Year& y);//可能月日年顺序错,可能传递无效的月份或日期
...
};
Date d(Month(3), Day(30), Year(1995));//可有效防止接口误用保证了类型精确之后,需要保证输入的值有用:
class Month {
public:
static Month Jan() { return Month(1); }
static Month Feb() { return Month(2); }
...
static Month Dec() { return Month(12); }
...
private:
explicit Month(int m);
...
};
Date d(Month::Mar(), Day(30), Year(1995));规定能做和不能做的事
if ( a * b = c) ...//以const修饰操作符*,使其不能被赋值提供举动一致的接口
为了避免忘记删除或者重复删除指针,可令工厂函数直接返回智能指针:
Investment* createInvestment(); //用户可能忘记删除或者重复删除指针
std::tr1::shared_ptr<Investment> createInvestment();若盼望用自定义的getRidOfInvestment,则需要避免误用delete,可思量将getRidOfInvestment绑定为删除器(deleter):
删除器在引用次数为0时调用,故可创建一个null shared_ptr
std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0),
getRidOfInvestment);//创建一个null shared_ptr
retVal = ... ;//令retVal指向目标对象
return retVal;
}若pInv管理的原始指针能在pInv创立之前确定下来,则将原始指针直接传递给pInv的构造函数更好
tr1::shared_ptr会主动使用每个指针专属的删除器,从而无须担心cross-DLL problem:
cross-DLL problem:对象在动态毗连程序库(DLL)中被new创建,但在另一个DLL内被delete销毁
//返回的tr1::shared_ptr可能被传递给任何其他DLL
//其会追踪记录从而在引用次数为0时调用那个DLL的delete
std::tr1:;shared_ptr<Investment> createInvestment()
{
return std::tr1::shared_ptr<Investment>(new Stock);
}Boost的tr1::shared_ptr特点:
[*]是原始指针的两倍大
[*]以动态分配内存作为簿记用途和删除器的专属数据
[*]以virtual形式调用删除器
[*]在多线程程序修改引用次数时有线程同步化(thread synchronization)的额外开销
Tips:
[*]好的接口不易被误用
[*]促进精确使用的方法包括接口一致性和与内置类型的举动兼容
[*]阻止误用的办法包括建立新类型、限制类型上的操纵、束缚对象值、消除客户的资源管理责任
[*]tr1::shared_ptr支持定制型删除器(custom deleter),这可以防范DLL题目,可被用来主动解除互斥锁(mutexes)等
条款19:设计class犹如设计type(Treat class design as type design)
定义一个新class时也就定义了一个新type。设计高效的类需要思量以下题目:
[*]新type的对象应如何创建和销毁(第8章))
[*]影响构造函数和析构函数、内存分配函数和开释函数(operator new,operator new [],operator delete,operator delete [])
[*]对象的初始化和赋值应有什么差别(条款4)
[*]决定构造函数和赋值操纵符的举动
[*]新type的对象如果被pass-by-value意味着什么
[*]由copy构造函数定义pass-by-value如何实现
[*]什么是新type的合法值
[*]有用的数值集决定了类必须维护的约束条件(invariants),
[*]进而决定了成员函数(特别是构造函数、析构函数、setter函数)的错误查抄
[*]还影响函数抛出的非常和少少使用的函数非常明细列(exception specifications)
[*]新type需要共同某个继承图系(inheritance graph)吗
[*]继承既有的类,则受那些类束缚,尤其要思量那些类的函数是否为虚函数
[*]被其他类继承,则影响析构函数等是否为virtual
[*]新type需要什么样的转换
[*]若允许类型T1隐式转换为类型T2,可可思量:
[*]在T1类内写类型转换函数(operator T2)
[*]在T2类内些non-explicit-one-argument(可被单一实参调用)的构造函数
[*]若只允许explicit构造函数存在,就得写专门执行转换的函数,且没有类型转换操纵符(type conversion operators)或non-explicit-one-argument构造函数
[*]什么样的操纵符和函数对于此新type合理
[*]决定需要声明哪些函数,其中哪些是成员函数
[*]什么样的标准函数应驳回
[*]这些必须声明为private
[*]谁改取用新type的成员
[*]影响public、private、protected的选择
[*]影响友元类、友元函数、及其嵌套的设计
[*]什么是新type的未声明接口(undeclared interface)
[*]要思量其对效率、非常安全性、资源运用的保证
[*]新type有多么一般化
[*]若要定义整个type家族,则应该定义新的class template
[*]是否真的需要新type
[*]若定义新的派生类就足够,则大概定义non-member函数或templates更好
Tips:
[*]Class设计就是type设计,需要思量以上所有题目
条款20:宁以pass-by-reference-to-const更换pass-by-value(Prefer pass-by-reference-to-cons to pass-by-value)
避免构造和析构
class Person {
public:
Person();
virtual ~Person();
...
private:
std::string name;
std::string address;
};
class Student: public Person {
public:
Student();
~Student();
...
private:
std::string schoolName;
std::string schoolAddress;
};
bool validateStudent(Student s);//会调用六次构造函数和六次析构函数
bool validateStudent(const Student& s);//效率提升很多上述代码validateStudent函数中pass-by-value会调用六次构造函数和六次析构函数:
[*]Student构造+Person构造+Student的2个string+Person的2个string
[*]析构同理
使用pass-by-reference可避免频繁构造和析构
避免对象切割
对象切割(slicing):派生类以值传递并被视为基类对象时,回调用基类的构造函数,而派生类的成分全无
class Window {public: ... std::string name() const;//返回窗口名称 virtual void display() const;//显示窗口和其内容};class WindowWithScrollBars: public Window {public: ... virtual void display() const;};void printNameAndDisply(Window w){ std::cout
页:
[1]