Effective C++ 条款15学习笔记:在资源管理类型中提供对原始资源的访问

本文探讨了如何通过智能指针如auto_ptr和shared_ptr访问原始资源,对比了显式转换与隐式转换的不同方式,并讨论了它们在资源管理中的应用及潜在问题。

Provide access to raw resources in resource-managing classes

1.如何访问原始资源

在上两条款我们知道如何使用智能指针管理我们的申请的资源,但是读者是否发现,我们如何去访问我们原始资源的方法呢?在这一条款将得到解答以及类型转换的相关问题。
首先,我们用代码来说话吧,及输出的结构看到
// pro_acce.cpp : 定义控制台应用程序的入口点。 //2011/9/21 by wallwind on sunrise; #include "stdafx.h" #include <iostream> #include <memory> using namespace std; class myClass { public : myClass() { cout<<"myClass()"<<endl; } ~myClass() { cout<<"~myClass()"<<endl; } void printFunc() { cout<<"printFunc()"<<endl; } int getType()const { return type; } private: int type; }; myClass* creatMyClass() { myClass *my=new myClass(); return my; } void issue(myClass *my) { delete my; } int _tmain(int argc, _TCHAR* argv[]) { auto_ptr<myClass> apMy(creatMyClass()); myClass* myC=apMy.get();//////auto_ptr 给我们提供的函数,用来访问原始资源 myC->printFunc();//////调用了myClass的方法 return 0; }


图一
从这里我们可以看到,程序输出了我们想要的结果。如书中所述:

tr1::shared_ptr 和 auto_ptr 都提供了一个 get 成员函数来进行显式转换,也就是说,返回一个智能指针对象中的裸指针(的副本):

myClass* myC=apMy.get();//////

似乎所有的智能指针类,包括 tr1::shared_ptr 和 auto_ptr 等等,都会重载指针解析运算符( operator-> 和 operator* ),这便使得对原始裸指针进行隐式转换成为现实,在这里我就不实际举例子了。下面使用书中的片段代码来说明一下问题吧:

std::tr1::shared_ptr<Investment> pi1(createInvestment()); // 使用 tr1::shared_ptr // 管理资源 bool taxable1 = !(pi1->isTaxFree()); // 通过 operator-> 访问资源 ... std::auto_ptr<Investment> pi2(createInvestment()); // 使用 auto_ptr 管理资源 bool taxable2 = !((*pi2).isTaxFree()); // 通过 operator* 访问资源


出色的资源管理类型可以避免资源泄露并有效的管理资源,但世界并非是如你所愿的。当某个API需要使用资源管理类型把持的原始资源时,这样的麻烦又会随之而来。

也许您注意到了
void issue(myClass *my) { delete my; } 这个函数没有使用到,那么当你使用这个时候 int _tmain(int argc, _TCHAR* argv[]) { auto_ptr<myClass> apMy(creatMyClass()); issue(apMy.get()); return 0; }


就出现了问题。结果大家可以试一下。
2.隐式转换
因为有时候要取得RAII对象内原始资源, RAII设计者使用了一种隐式转换函数,

如果让资源管理类型提供隐式转换函数,可以让行为变的更自然,但这样的作法没有好下场,只会增加客户端发生错误的机率。比如下面的代码(简单的编写了一个自定义的AutoPtr,它重载了隐式转换operator):

#include "stdafx.h" #include <stdlib.h> #include <memory> #include <string> using namespace std; template<typename T> class AutoPtr { public: AutoPtr(T *tP) : _tP(tP), _released(false) { } ~AutoPtr() { Release(); } T* operator->() { return _tP; } operator T*() const { return _tP; } void Release(void) { if (!_released) { delete _tP; _released = true; } } private: T *_tP; bool _released; }; typedef struct Point { double X; double Y; }; void PrintPoint(Point *pTP) { } int _tmain(int argc, _TCHAR* argv[]) { AutoPtr<Point> apI(new Point()); PrintPoint(apI); Point *pTP = apI; apI.Release(); system("pause"); return 0; }


pTP也获得了apI提供的Point指针,当apI作用域结束后或显示调用了Release方法,pTP也就成了迷途指针了。通常来说提供Get方法是比较大众切容易接受的正确方法,它将隐式转换所带来的恶意最小化。
也许在诸如auto_ptr、shared_ptr只提供原始指针的访问违背了面向对象的封装性,可能是令人产生误解和迷惑的地方。因为RAII classes并不是为了封装某物而存在的,更多的它只是一种模版编程的概念。

#include "stdafx.h" #include <stdlib.h> #include <memory> #include <string> using namespace std; template<typename T> class AutoPtr { public: AutoPtr(T *tP) : _tP(tP), _released(false) { } ~AutoPtr() { Release(); } T* operator->() { return _tP; } operator T*() const { return _tP; } void Release(void) { if (!_released) { delete _tP; _released = true; } } private: T *_tP; bool _released; }; typedef struct Point { double X; double Y; }; void PrintPoint(Point *pTP) { } int _tmain(int argc, _TCHAR* argv[]) { AutoPtr<Point> apI(new Point()); PrintPoint(apI); Point *pTP = apI; apI.Release(); system("pause"); return 0; }


pTP也获得了apI提供的Point指针,当apI作用域结束后或显示调用了Release方法,pTP也就成了迷途指针了。通常来说提供Get方法是比较大众切容易接受的正确方法,它将隐式转换所带来的恶意最小化。
也许在诸如auto_ptr、shared_ptr只提供原始指针的访问违背了面向对象的封装性,可能是令人产生误解和迷惑的地方。因为RAII classes并不是为了封装某物而存在的,更多的它只是一种模版编程的概念。

牢记在心

l API 通常需要访问原始资源,因此每个 RAII 类都应该提供一个途径来获取它所管理的资源。

l 访问可以通过显式转换或隐式转换来实现。一般情况下,显式转换更安全,但是隐式转换对于客户端程序员来说使用更方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值