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

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、付费专栏及课程。

余额充值