条款14:在资源管理类中小心copy行为
当我们深入理解“资源取得时机是初始化时机(RAII)”概念,并以此作为“资源管理类”的核心时,我们可能会遇到将RAII对象复制的情况,一般有两种情况处理这个现象:1)如果我们的RAII对象是唯一的,那么复制就不合理,因此我们应该禁止copy构造的行为,正确的处理方式:将copy函数定义为private(base class),并用derived class private继承这个函数来阻止copy行为; 2)使用引用计数法,即智能指针的形式来实现对所有拷贝资源的追踪,但是智能指针的缺省行为是当引用次数为0时删除其所指向的对象。如果我们要实现的功能不是释放资源,这时候就应该自定义智能指针的deleter(一个函数或者函数对象),当引用次数为0时调用这个函数实现相应功能,如:
class lock{
public:
explicit lock(mutex *pm) : mutexptr(pm,unlock) { } //用mutex初始化shared_ptr,并以unlock函数为删除器
private:
std::tr1::shared_ptr<mutex>mutexptr;
};
另外,上述例子中不需要声明析构函数,因为class 析构函数会自动调用其non-static成员变量的析构函数;3)复制底部资源,采用深度拷贝的方法;4)采用转移底部资源拥有权的方法,也就是所谓的auto_ptr指针;
条款15:在资源管理类中提供对原始资源的访问
当你需要访问一个RAII class对象的内部原始资源时,有两种方法可以达成目标:1)在类的内部提供一个get成员函数,用来执行显示转换,也就是返回类内部指针的原始复件;2)允许隐式转换到底部原始指针;在智能指针中,重载了指针取值操作符(operator->和operator*)来实现隐式转换;对于一般的来说,可以在类中提供一个隐式转换函数;如:
class font{
public: //显示转换函数:
explicit font(fonthandle fh) : f(fh){ } fonthandle get() const { return f; }
~font() { releasefont(f); } //隐式转换函数
private: operator fonthandle() const {return f;}//没太明白怎么隐式转换的
fonthandle f; //operator起到什么作用?
}
//显示转换 //隐式转换
void changefontsize(fonthandle f,int newsize); void changefontsize(fonthandle f,int newsize);
font f(getfont()); font f(getfont());
changefontsize ( f.get(), newfontsize); changefontsize ( f, newfontsize);
//但隐式转换会出一个问题
font f1(getfont());
fonthandle f2=f1; //原意是拷贝,结果是f1先转化为底部的fonthandle,然后拷贝,一旦此时f1被销毁,f2就处于指向资源被释放的状态(dangle)
条款16:成对使用new和delete时采取相同的方式
如果你在new的表达式中使用[ ],则必须在相应的delete表达式中也使用[ ],如果没有使用[ ],那么也一定不要在delete中使用[ ];
条款17:以单独语句将newed对象置入智能指针中
对于以下代码分析;
int priority( );
void processwidget(std :: tr1 :: shared_ptr<wideget>pw,int priority);
//调用时 processwidget(new widget, priority( ));//编译错误,shared_ptr构造函数是explicit类型,不允许隐式转换
//processwidget(std :: tr1 :: shared_ptr<wideget>(new widget),priority( ) );
这个函数在调用processwidget之前,编译器必须创建代码,完成以下三件事情:1)调用priority;2)执行new widget;3)调用 std::tr1::shared_ptr构造函数;但是1)和2)的执行顺序和编译器有关,如果先执行2)后执行1),且在执行1)的时候发生异常,那么new widget返回的指针就会失效,那么内存泄露就产生了;
解决上述问题的方法:使用分离语句,把创建对象和给智能指针赋初值的语句分离出来,原因是:编译器对于“跨越语句的各项操作”没有重新排列的自由(只有在语句内才拥有那个自由度);代码如下:
std::tr1::shared_ptr<widget>pw(new widget);
processwidget(pw,priority( ));