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

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

2011-09-22 23:15 531 查看
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 访问可以通过显式转换或隐式转换来实现。一般情况下,显式转换更安全,但是隐式转换对于客户端程序员来说使用更方便。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐