11. 在operator= 中处理自我赋值
看下面代码感受自我赋值的危险
class Bitmap{...}
class Widget{
...
private:
Bitmap *pb;
}
Widget& Widget::operator=(const Widget&rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
如果上述代码new Bitmap时出错,抛出异常,那么就会出现this对象的pb已经被释放掉了,导致return了一个野指针,不安全。
解决方法:在复制pb所指东西之前不delete pb
Widget& Widget::operator=(const Widget&rhs)
{
Bitmap *originPb = rhs.pb;
pb = new Bitmap(*rhs.pb);
delete originPb;
return *this;
}
或者用copy and swap技术
Widget& Widget::operator=( Widget rhs)
{
swap(rhs);
return *this;
}
总结:
确保对象自我赋值是有良好行为,相关技术有:“证同测试”、在成功new之后再delete 被赋值的指针、copy and swap技术,从而实现任何函数操作一个以上对象,并且这些对象都是继承同一个基类对象时,行为仍然正确。
12. 复制对象时勿忘其每个成分
当自己写copy构造函数和copy赋值操作符(下称为copying函数)定义时,如果遗漏了对某个成员变量复制,编译器不会提醒。所以当自己写copying函数时,只要在类中添加了一个成员变量,必须同时修改coping函数和自己写的构造函数&赋值操作符。
总结:当自己写copying函数,确保复制了所有类的local成员变量,并调用所有基类内的适当copying函数。
13. 以对象管理资源
1)资源一旦使用了,将来不用的时候就要还给系统。资源包括:动态分配内存,文件描述器,互斥锁,图形界面中的字形和笔刷,数据库连接和网络sockets。
2)把资源放进对象内,便可依赖于析构函数自动调用机制确保资源被释放。
3)“以资源管理资源”的2个关键想法:
- RAII。资源取得时机就是初始化时机
- 管理对象运用析构函数确保资源被释放。
4)auto_ptr若发生复制行为,则将唯一的资源拥有权转移出去。而share_ptr发生复制行为,只是把资源拥有权复制一份。但两者的析构函数都是执行delete操作,若要想析构函数执行delete操作,那么需要使用boost::scoped_arraty和boost::shared_array
总结:为防止资源泄漏,使用RAII对象,即使其在构造函数中获取资源并在析构函数中释放资源。常用的RAII对象有tr1::shared_ptr和auto_ptr。
14. 在资源管理类中小心coping行为
赋值RAII对象必须一并赋值它所管理的资源,因此资源的copying行为决定RAII对象的copying行为。
RAII class copying行为有:不给copying或向shared_ptr一样实现引用计数法或复制底部资源。
15.在资源管理类中提供对原始资源的访问
有时候形参要求是某类的指针,这是可以用tr1::shared_ptr和auto_ptr提供的get成员函数执行显式转换,返回的是智能指针内部的原始指针的复件。
总结:APIs总会要求访问原始资源,所以如果是自己实现RAII类,那么应该提供一个区的原始指针的方法。
16. 成对使用new和delete时要采取相同形式
总结:如果调用new时使用[],那么对应调用delete时使用[];如果调用new是没有用[],那么对应调用delete时不用[]。即new [] --> delete [] ; new --> delete;
PS:为避免出现不可预料错误,尽量不要对数组形式做typedefs动作。
eg:
typedef std::string AddressLines[4];
std::string *pal = new AddressLines;
delete pal; // error
delete[] pal; // right
17. 以独立语句将newed对象置入智能指针
因为不清楚编译器以何种次序将new的对象放入智能指针,所以要使用分离语句,先new指针,然后将它置入智能指针内,最后将智能指针传给函数。