您的位置:首页 > 编程语言 > C语言/C++

Effective C++ rule 13 用对象管理资源

2017-08-03 11:19 330 查看

前言

在C++里面,我们经常会像系统申请一些资源,这些资源包括最常见的动态分配的内存,已经文件描述符,互斥锁,套接字等等。很多时候,我们需要确保在任何情况下 在不使用这些资源的时候都及时释放资源。不然将会造成内存泄漏等等的原因。

我们可以编写逻辑严密的代码来保证资源在不被使用时能够及时释放,当时当考虑到异常的发生,函数多返回路径,其他同事未能充分理解代码而加入某些continue/break/return 时,此时并不简单.

这是一件任重道远的事情。

为了解决这样子的问题,人们提出利用栈对象来管理资源的思想。其核心思想主要有两点:

1. 将资源托管给栈对象,由栈对象提供简单的接口来实现对资源的访问,需要保证一获取资源就将资源放入对象内。

2. 依赖于编译器对栈对象的自动回收来实现资源的回收。例如函数作用域的局部对象在函数的任意一个结束点,不管是正常结束还是异常结束,只要离开栈对象所在的作用域,系统就会调用栈对象的析构函数来回收该对象。

我们利用第二点,将资源“绑定”到栈对象上,试图在栈对象的析构函数里面释放资源,这样当栈对象析构时 与其绑定的资源也会被释放掉。

举个栗子

Code1.1

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
class Mat
{
public:
Mat(int n, int m)
//构造函数,生成nxm的矩阵
{
_mat = (double**)malloc(n*sizeof(double*));
for (int i = 0; i < n; i++)
{
_mat[i] = (double*)malloc(m*sizeof(double));
}
row = n;
column = m;
}
~Mat()
{
printf("call ~Mat()\n");
for (int i = 0; i < row; i++)
{
free(_mat[i]);
}
free(_mat);
}
public:

private:
double ** _mat;//Mat使用一个double **来指代
int row, column;
};
void func()
//动态申请资源,手动释放
{
printf("func()\n");
Mat* m = new Mat(2, 2);
//各种计算····
存在的问题:
//1.本来最后就忘记释放m了。糟了
//2.部分return 的之前有释放m,但是不是所有return 之前都有释放,也就是没有覆盖所有的情况。
//3.没有考虑异常抛出的时候,释放m
}
void func1()
//栈分配对象资源,无论函数怎么结束,自动释放对象,该方法简单粗暴的解决了上面函数的问题,但是有些资源是不适合用来当作栈变量的,比如那些需要很大内存开销的对象···
{
printf("func1()\n");
Mat m(2, 2);
//各种语句
//函数结束时自动释放
}
void func2()
//动态分配资源,但是将资源交付给通用的栈对象去管理,该方法最灵活,也最好定制一些功能
{
printf("func2()\n");
auto_ptr<Mat> pMat(new Mat(2,2));
//各种语句···
//函数结束时,因为pMat是栈对象,pMat会被释放掉,释放的过程中,pMat指代的实际资源也就被释放了。
}
int main()
{
func();
func1();
func2();
system("pause");
return 0;

}


输出:

func()
func1()
call ~Mat()
func2()
call ~Mat()


分析:

从输出我们可以知道,func1()和func2()都可以保证~Mat()的执行,而且这个调用时机是由系统控制的。但是func1()的方式在处理诸如Mat(1e6,1e6)这样的内存消耗大户的时候就很吃力了。

C++现有的”通用”对象管理器——智能指针

auto_ptr

auto_ptr是C++ STD自带的智能指针模板类,其核心代码如下:

template<class _Ty>
class auto_ptr
{   // wrap an object pointer to ensure destruction

private:
_Ty *_Myptr;    // 对象的指针

public:
typedef auto_ptr<_Ty> _Myt;
typedef _Ty element_type;

_Ty *get() const _THROW0()
{   // return wrapped pointer
return (_Myptr);
}

_Ty *release() _THROW0()
{   // return wrapped pointer and give up ownership
_Ty *_Tmp = _Myptr;
_Myptr = 0;//将自己的_Myptr置空,但是没有释放。
return (_Tmp);
}

void reset(_Ty *_Ptr = 0)
{   // destroy designated object and store new pointer
if (_Ptr != _Myptr)
delete _Myptr;
_Myptr = _Ptr;
}

explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
: _Myptr(_Ptr)
{   // 由对象的指针来初始化auto_ptr对象
}

auto_ptr(_Myt& _Right) _THROW0()
: _Myptr(_Right.release())
{   // construct by assuming pointer from _Right auto_ptr
//复制构造函数,由另外一个auto_ptr对象复制过来,会调用release,然后再调用_Myptr(_Ty *)构造函数
}

auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
{   // construct by assuming pointer from _Right auto_ptr_ref
_Ty *_Ptr = _Right._Ref;
_Right._Ref = 0;    // release old
_Myptr = _Ptr;  // reset this
}

_Myt& operator=(_Myt& _Right) _THROW0()
{   // assign compatible _Right (assume pointer)
//赋值运算,也会释放Right的指针
reset(_Right.release());
return (*this);
}
~auto_ptr() _NOEXCEPT
{   // destroy the object
delete _Myptr;
}

_Ty& operator*() const _THROW0()
{   // return designated value
return (*get());
}

_Ty *operator->() const _THROW0()
{   // return pointer to class object
return (get());
}
};


我们可以看到,auto_ptr的核心功能主要是

1. 时刻保证最多只有一个auto_ptr指向对象

2. auto_ptr的析构函数~auto_ptr负责delete掉_Myptr所指向的对象,而_Myptr的申请由用户自己负责。

因此,我们可以使用auto_ptr来完成常用的资源管理任务,但是auto_ptr有一个性质就是“时刻保证最多只有一个auto_ptr对象指向目标对象”,因此auto_ptr之间的copy并不是我们通常所想的那样的。如果我们不想有这个特性,那么就需要自己编写资源管理类了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: