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;
}
如果让资源管理类型提供隐式转换函数,可以让行为变的更自然,但这样的作法没有好下场,只会增加客户端发生错误的机率。比如下面的代码(简单的编写了一个自定义的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;
}
也许在诸如auto_ptr、shared_ptr只提供原始指针的访问违背了面向对象的封装性,可能是令人产生误解和迷惑的地方。因为RAII classes并不是为了封装某物而存在的,更多的它只是一种模版编程的概念。
牢记在心
l API 通常需要访问原始资源,因此每个 RAII 类都应该提供一个途径来获取它所管理的资源。
l 访问可以通过显式转换或隐式转换来实现。一般情况下,显式转换更安全,但是隐式转换对于客户端程序员来说使用更方便。