C++的资源:
- 动态分配内存
- 文件描述器
- 数据库连接
- 网络sockets
- 互斥锁
- 图形界面中的字型和笔刷
以对象管理资源(又称为资源取得时机便是初始化时机,resource acquisition is initialization,RAII)
单纯依靠调用端执行delete语句是行不通的:
一般调用端函数使用了函数(Create函数)返回的对象后,有责任删除之。但在以下情况下可能使得调用端函数来不及删除
- 一个过早的return语句
- 一个存在continue和goto语句的循环
- 某个语句抛出异常
为确保Create函数返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开调用端函数,该对象的析构函数会自动释放资源
例1:auto_ptr
- Header: < memory >
Namespace: std
- 是一个类指针对象,也就是智能指针,其析构函数自动对其所指对象调用delete
void f() { std::auto_ptr<Investment> pInv(createInvestment()); //利用create函数获得资源后立即放进对象pInv ... }//经由auto_ptr的析构函数自动删除pInv
PS:对象被删除一次以上,会使程序出现“未定义行为”
- 诡异的复制行为:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权,因此,受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它。
std::auto_ptr<Investment> pInv1(createInvestment()); std::auto_ptr<Investment> pInv2(pInv1);//现在pinv2指向对象,pInv1被设为NULL pInv1 = pInv2; //现在pInv1指向对象,pInv2被设为NULL
例2:引用计数型智慧指针(reference-counting smart pointer,RCSP)shared_ptr
- Header: < memory >
Namespace: std::tr1
也是智能指针,持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源。
void f()
{
std::tr1::shared_ptr pInv(createInvestment());
//利用create函数获得资源后立即放进对象pInv,shared_ptr是一个RCSP
…
}//经由shared_ptr的析构函数自动删除pInvRCSP无法打破环状引用(例如两个其实已经没被使用的对象彼此互指,因而好像处于“被使用”状态)。
缺省行为是“当引用次数为0时删除其所指”,std::tr1::shared_ptr也允许指定“删除器”,是一个函数或函数对象,当引用次数为0时便被调用,删除器对std::tr1::shared_ptr构造函数而言是可有可无的地儿参数(此机能不存在于auto_ptr,它总是将其指针删除)
class Lock{ public: explicit Lock(Mutex* pm):mutexPtr(pm,unlock){}//以unlock函数为删除器 private: std::tr1::shared_ptr<Mutex> mutexPtr; };
- 复制
std::tr1::shared_ptr<Investment> pInv1(createInvestment()); std::tr1::shared_ptr<Investment> pInv2(pInv1);//pinv2,pInv1指向同一对象 pInv1 = pInv2; //pinv2,pInv1指向同一对象
在auto_ptr和shared_ptr的析构函数内做delete而不是delete[],因此虽然可以通过编译,在动态分配而得的array身上使用是馊主意。
在复制资源管理对象时,进行的是“深度拷贝”
对于非heap-based资源,像auto_ptr和tr1::shared_ptr智能指针往往不适合作为资源掌管者
PS : TR1(Technical Report 1)是一份规范,描述加入C++标准程序的诸多新技能大多数TR1机能是以Boost的工作为基础的。TR组件位于std::tr1命名空间中。大多数TR1机能是以Boost的工作为基础的。TR组件位于std::tr1命名空间中。
在资源管理类中应提供对原始资源的访问
举例:
std::tr1::shared_ptr<Investment> pInv(createInvestment());
...
int fun(const Investment* pi);
...
int x = fun(pInv);//错误,所需参数是Investment*指针,但是传递的是tr1::shared_ptr<Investment>对象
解决方法:
显式转换(比较安全)
tr1::shared_ptr和auto_ptr都提供一个get成员函数执行显式转换。也就是它会返回智能指针内部的原始指针(的复件):
int x = fun(pInv.get());
隐式转换(对客户比较方便)
同所有智能指针一样,tr1::shared_ptr和auto_ptr也重载指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:
class Investment{ public: bool isfun() const; ... }; Investment* createInvestment(); std::tr1::shared_ptr<Investment> pi1(createInvestment()); bool isf1 = !(pi1->isfun()); ... std::tr1::shared_ptr<Investment> pi1(createInvestment()); bool isf1 = !((*pi1).isfun());
提供隐式转换函数