资源管理
条款13: 以对象管理资源
【Problem】
void f()
{
Investment *pInv = createInvestment();
... /* 这里存在诸多“不定因素”,造成 "delete pInv"得不到执行,存在内存泄露的可能。*/
delete pInv;
}
【Solution. 1】
使用auto_ptr智能指针确保资源的释放,避免内存泄露
void f(){
std::auto_ptr<Investment> pInv(createInvestment());
...
/*函数退出之前,经由auto_ptr的析构函数自动删除pInv指针*/
}
【Key Points】
“以对象管理资源”的两个关键想法:
1. 获得资源后立刻放进管理对象内(如auto_ptr)
2. 管理对象运用析构函数确保资源被释放
【Attention】
auto_ptr的限制,举例:
std::auto_ptr<Investment>
pInv1(createInvestment()); //pInv1指向createInvestment()返回值;
std::auto_ptr<Investment>
pInv2(pInv1); //pInv2指向对象,而pInv1被设为NULL;
pInv1 = pInv2; //pInv1指向对象,而pIn2被设为NULL;
受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它。
STL容器要求其元素发挥正常的复制行为,因此这些容器容不得auto_ptr。
【Solution.2】
使用share_ptr智能指针(引用计数型智慧指针)
void f()
{
...
std::tr1::shared_ptr<Investment>
pInv1(createInvestment()); //pInv1指向createInvestment()返回物;
std::tr1::shared_ptr<Investment>
pInv2(pInv1); //pInv1,pInv2指向同一个对象;
pInv1 = pInv2; //同上,无变化
...
} //函数退出,pInv1,pInv2被销毁,它们所指的对象也竟被自动释放。
【Attention】
1. auto_ptr和tr1::share_ptr 两者都是在其析构函数内做
delete
而不是
delete[];
意味着在动态分配的array身上使用两者是个坏主意。
2. share_ptr不能管理循环引用的对象,否则造成内存泄露,举例:
class parent;
class child;
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<child> child_ptr;
class parent
{
public:
children_ptr child;
~parent() { std::cout <<"destruct parent."<<std::endl; }
};
class child
{
public:
parent_ptr parent;
~child() { std::cout <<"destruct child."<<std::endl; }
};
int main(){
parent_ptr pat(new parent()); //(pat 计数 = 1)
children_ptr chd(new child()); //(chd 计数 = 1)
pat->child = chd; //(chd 计数 = 2)
chd->parent = pat; //(pat 计数 = 2)
return 0; // (函数退出时,减一后仍然大于0,无法执行析构函数,造成内存泄露)
}
解决方法:
1. 当parent的生存期超过childr的生存期的时候,child内改为使用一个普通指针指向parent。
2. 使用弱引用的智能指针打破这种循环引用。举例:
暂略
条款14: 在资源管理类中小心coping行为
【Attention】
1. 复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为
2. 普遍而常见的RAII类拷贝行为是:抑制拷贝,施行引用计数法。不过其它行为也可能被实现
【Solution】
“当一个RAII对象被复制,会发生什么事?”大多数时候你会选择一下两种可能:
1. 禁止复制。如果复制动作对RAII类并不合理,你便应该禁止之。利用条款6(禁止类的copying函数)。
2. 对底层资源使用”引用计数法“。有时候我们又希望保有资源,直到它的最后一个使用者被销毁。 利用shared_ptr
3. 转移底部资源的拥有权。 利用auto_ptr
4. 复制底部资源。 利用深拷贝
``
条款15:在资源管理类中提供对原始资源的访问
【Problem】
需要一个函数可将RAII对象(shared_ptr)转换为其所内含之原始资源.
【Solution】
有两种做法可以达成目标:
1. 显示转换
shared_ptr和auto_ptr提供一个get成员函数,用来执行显示转换,也就是返回智能指针内部的原始指针。
shared_ptr和auto_ptr重载指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针。
(即在对智能指针对象实施->和*操作时,实际被转换为被封装的资源的指针。)
2. 隐式转换
隐式转换可能引起“非故意之类型转换”
RAII class 设计方式
class Font
{
public:
...
FontHandle get() const//FontHandle是资源;显示转换函数
{
return f;
}
operator FontHandle() const//隐式转换这个值得注意,可能引起“非故意之类型转换”
{
return f;
}
private:
FontHandle f; // 资源
...
};
尽量使用显示转换,减少出错的可能
条款16:成对使用new和delete时要采取相同形式
如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。
如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。