Part1一、让自己习惯C++
条款01:视C++为一个语言联邦
条款02:尽量以const,enum,inline替换 #define
#define ASPECT_RATIO 1.653
1. 如果你的编译器支持在类内对const static 整数类型声明时获初值,则使用
2. 如果不支持,则在类内定义,在对应的实现文件中赋值
条款 03:尽可能使用const
1. 他们使class接口比较容易理解
2. 他们使得可以操作const对象
条款 04:确定对象被使用前已先被初始化
class FileSystem{...};
FileSystem& tfs(){
static FileSystem fs;
return fs;
}
Part2二、构造/析构/赋值运算
条款05:了解C++默默编写并调用了哪些函数
1. 类中含有**引用**的成员变量
2. 类中含有**const**的成员变量
3. 类的**基类**中的拷贝赋值运算符是**私有**成员函数
条款06:若不想使用编译器自动生成的函数,就应该明确拒绝
条款07:为多态基类声明virtual析构函数
1. 用来作为带有多态性质的基类的类
2. 一个类中带有任何virtual函数
条款08:别让异常逃离析构函数
条款09:绝不再构造和析构函数中调用virtual函数
解决办法之一:
class Transaction{
publci:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logIngo) const;//把它变成这样的non-virtual函数
...
};
Transaction::Transaction(const std::string& logInfo){
...
logTransaction(logInfo);//这样调用
}
class BuyTransaction: public Transaction{
BuyTransaction( parameters ):Transaction(createLogString( parameters )){...}//将log信息传给基类的构造函数
private:
static std::string createLogString( parameters );//注意此函数为static函数
}
条款10:令operator= 返回一个reference to *this
条款11:在operator= 中处理“自我赋值”
Widget::operator=(const Widget& rhs){
delete pb; //把自己释放了
pb = new Bitmap(*rhs.pb);//这就不安全了
return *this;
}
先验证是不是相同的,是不是自我赋值
Widget::operator=(const Widget& rhs){
if(this == &rhs) return *this;//验证是不是相同
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
在复制pb所指的东西之前别删除pb
Widget::operator=(const Widget& rhs){
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);//让pb指向*pb的一个副本
delete pOrig; //删除原先的pb
return *this;
}
使用交换数据的函数
class Widget{
...
void swap(Widget& rhs);//交换*this和rhs的数据
...
};
Widget::operator=(const Widget& rhs){
Widget temp(rhs);//创建一个rhs副本
swap(temp);//交换*this和上面的副本
return *this;
}
条款12:复制对象时勿忘其每一个成分
Part3三、资源管理
条款13:以对象管理资源
条款14:在资源管理类中心copying行为
1. 当我们处理不能同步拥有的资源的时候,可以才用**禁止复制**,如把copying操作声明为private
2. 当我们希望共同拥有资源的时候,可以采用**引用计数法**,例如使用shared_ptr
3. 当我们需要拷贝的时候,可以采用**深拷贝**
4. 或者某些时候我们可以采用**转移**底部资源拥有权的方式
条款15:在资源管理类中提供对原始资源的访问
class Font{
...
FontHandle get() const {return f;} //显示转换
...
operator FontHandle() const {return f;} //隐式转换函数
....
private:
FontHandle f; //管理的原始资源
}
条款16:成对使用new和delete时要采用相同形式
条款17:以独立语句将newed对象置入智能指针
诸如这样的语句processWidget (std::tr1::shared_ptr
1. 在先执行new Widget`语句和调用std::tr1::shared_ptr构造函数之间
2. 不能确定priority函数的执行顺序,可能在最前面,也可能在他们的中间
Part4四、设计与声明
条款18:让接口容易被正确使用,不易被误用
条款19:设计class犹如设计type
1. 合理的构建class的构造函数、析构函数和内存分配函数以及释放函数
2. 不能把初始化和赋值搞混了
3. 如果你的类需要被用来以值传递,复制构造函数应该设计一个通过值传递的版本
4. 你应该给你的成员变量加约束条件,保证他们是合法值,所以你的成员函数必须担负起错误检查工作
5. 如果你是派生类,那么你应该遵守基类的一些规范,如析构函数是否为virtural
6. 你是否允许你的class有转换函数,,是否允许隐式转换。如果你只允许explicit构造函数存在,就得写出专门负责执行转换的函数
7. 想清楚你的类应该有哪些函数和成员
8. 哪些应该设计为私有
9. 哪个应该是你的friend,以及将他们嵌套与另一个是否合理
10. 对效率,异常安全性以及资源运用提供了哪些保证
11. 如果你定义的不是一个新type,而是定义整个type家族,那么你应该定义一个类模板
12. 如果只是定义新的字类以便为已有的类添加机制,说不定单纯定义一个或多个non-member函数或模板更好
条款20:宁以pass-by-reference-to-const替换pass-by-value
条款21:必须返回对象时,别妄想返回其reference
条款22:将成员变量声明为private
1. 为了保证一致性
2. 可以细微的划分访问和控制以及约束
3. 内部更改后不影响使用
protected并不比public更具封装性
条款23:宁以non-member、non-friend、替换member函数
条款24:若所有参数皆需要类型转换,请为此采用non-member函数
条款25:考虑写出一个不抛出异常的swap函数
Part5五、实现
条款26:尽可能延后变量定义式的出现时间
条款27:尽量少做转型动作
const_cast 通常被用来将对象的常量性转除。它也是唯一有此能力的转型操作符 dynamic_cast 主要用来执行“安全向下转型” ,也就是用来决定对某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作
reinterpret_cast 意图执行低级转型,实际动作(及结果)可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int转型为一个int。这一类转型在低级代码以外很少见。
static_cast 用来强迫隐式转换,例如将non-const对象转换为const对象,或将int转为double等等,它也可以用来执行上述多种转换的反向转换,例如将void* 指针转为 type 指针,将pointer-to-base 转为 pointer-ro-derived 。但它无法将 const 转为 non-const ——这个只有const_cast才能办到
旧式转型使用的时机是,当要调用一个explicit构造函数对一个对象传递给一个函数时,其他尽量用新式转型
请记住以下:
1. 如果可以的话,避免dynamic_cast转型,如果实在需要,则可以试着用别的无转型方案代替
2. 如果转型是必要的,那么应该把他隐藏于某个函数背后,客户随后可以调用该函数,而不是需要将转型放进自己的代码里
3. 宁可要新型转型,也不要使用旧式转型
条款28:避免返回handles指向对象内部成分
条款29:为“异常安全”而努力是值得的
1. 首先以对象管理资源可以阻止资源泄漏
2. 在你能实现的情况下,尽量满足以上的最高等级
条款30:透彻了解inlining 的里里外外
1. 隐喻的inline申请,即把定义写在class内部
2. 明确声明,即在定义式前加上关键字inline
条款31:将文件间的编译依存关系降至最低
1. 头文件和实现相分离,头文件完全且仅有声明式
2. 使用创建接口类
Part6六、继承与面向对象设计
条款32:确定你的public继承塑模出is-a关系
条款33:避免遮掩继承而来的名称
条款34:区分接口继承和接口实现
条款35:考虑virtual函数以外的其他选择
条款36:绝不重新定义继承而来的non-virtual函数
条款37:绝不重新定义继承而来的缺省参数值
条款38:通过复合塑模has-a或“根据某物实现出”
条款39:明智而审慎地使用private继承
条款40:明智而审慎地使用多重继承
Part7七、模板与泛型编程
条款41:了解隐式接口和编译期多态
条款42:了解typename的双重意义
templete<typename T>
class Derived:public Base<T>::Nested{ //基类列表中不可以加“typename”
public:
explicit Derived(int x): Base<T>::Nested(x){//mem.init.list中不允许“typename”
typename Base<T>::Nested temp; //这个是嵌套从属类型名称
... //作为一个基类修饰符需要加上typename
}
}
条款43:学习处理模板化基类内的名称
1. 在基类函数调用之前加上 this->
2. 使用 using 声明式 ,告诉编译器,请它假设这个函数存在
3. 指出这个函数在基类中,使用基类::函数的形式写出来(不推荐这个,因为如果是virtual函数,则 会影响动态绑定)
但是当有模板全特化的时候,确实使用的没有这个函数,那么依然会报错
条款44:将与参数无关的代码抽离出来
条款45:运用成员函数模板接受所有兼容类型
条款46:需要类型转换时请为模板定义非成员函数
Part8八、定制new和delete
条款49:了解new—handler的行为
1. 让更多内存可被使用。此策略的一个做法是,程序一开始就分配一大块内存,而后当其第一次被调用,将它释还给程序使用
2. 安装另一个new—handler。可以设置让其调用另一个new—handler来替换自己,用来做不同的事情,其做法是调用set_new_handler
3. 卸载new—handler,也就是将null指针传给set_new_handler,这样new在分配不成功时抛出异常
4. 抛出bad_alloc的异常。
5. 不返回,调用abort或exit
6. C++并部支持类的专属new—handler,但其实也不需要。你可以令每个类提供自己的set_new_handler和operator new即可
set_new_handler允许客户指定一个函数,在内存分配无法获得满足时调用。
Nothrow new是一个颇为局限的工具,因为它只适用于内存分配:后继的构造函数调用还是可能抛出异常
条款50:了解new和delete的合理替换时机
条款51:编写new和delete时需固守常规
条款52:写了placement new也要写placement delete
Part9九、杂项讨论
条款53:不要轻忽编译器的警告
条款54:让自己熟悉包括TR1在内的标准程序库
条款55:让自己熟悉Boost
往期推荐