ToB企服应用市场:ToB评测及商务社交产业平台
标题:
《Effective C++》第三版-4. 设计与声明(Design and Declarations)
[打印本页]
作者:
美食家大橙子
时间:
2024-5-19 00:23
标题:
《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):派生类以值传递并被视为基类对象时,回调用基类的构造函数,而派生类的成分全无
[code]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
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4