Roger(
roger2yi@gmail.com)
1. 当作类对象的“共享句柄”来使用
2. 在类内部提供资源管理服务 —— “为类提供资源的共享拷贝语义
(或称为浅拷贝
shadow copy
),并在正确的时间回收资源”
上述的两种方式一种是在类外部使用,一种是在类内部使用,然而这两种方式并不是非此即彼的关系,很多情况下我们都可以选择任意一种,选择其中一种理由通常是遵循“更容易被使用,更难以被误用”的原则。
当作类对象的“共享句柄”来使用
在这种使用方式下,结合Create Method模式使用会取得更好的效果
(关于
Create Method
模式,更多信息请看“模式与重构一书”)。
以下是一个使用场景,我们有一个类,类封装了一个底层资源的句柄和跟句柄相关的所有API调用
(句柄和
API
调用来自操作系统或者其它的
C
函数库)。
句柄在不再被使用的时候需要被释放
(析构函数是最适合做这件事的,不是吗?);
句柄的拷贝可能是一种危险的行为
(通常句柄的实现都是所谓的
Opaque pointer
不透明指针,也是说该指针实际是指向一块
API
操作的数据结构,只是该数据结构是不对外公开的,这也是所谓的
C
-
OO
风格),因为有可能导致悬挂句柄
(其实也是悬挂指针)或者由于释放的责任模糊而导致重复释放。
所以,我们用类封装底层资源句柄,但不为该类提供拷贝行为,而是通过外裹的share_ptr来提供共享拷贝语义。
下面是一个实际例子,来自我自己封装lcms库profile句柄的一个类
(
lcms
是一个开源用于色彩管理的
C
函数库,也是一个轻量级可用于学习
C-OO
编程的不错选择),cmsHPROFILE是lcms的profile
(特性文件)资源句柄。
KProfile.h
#pragma
once
#include " KProfileShPtr.h "
#include < lcms / lcms.h >
namespace milk
{
/* * The wrapper class of lcms's profile,
group related function together,
and use RAII idiom to manage resource. */
class PUREMILK_EXPORT KProfile : private boost::noncopyable
{
public :
~ KProfile( void );
/* * Create method, more information please refer to book
- Refactoring to Patterns. */
/* * Three basic create methods, from an existing handle,
file and memory. */
static KProfileShPtr createFromHandle(cmsHPROFILE);
static KProfileShPtr createFromFile( const char * , const char * );
static KProfileShPtr createFromMemory( void * , UINT32);
/* * Get the internal profile handle of lcms. */
cmsHPROFILE getHandle() const { return handle_;}
/* * Print out */
ostream & print(ostream & ) const ;
/* * Basic information related function. */
string getProductName() const ;
string getProductDescription() const ;
string getProductInformation() const ;
string getManufacturer() const ;
string getModel() const ;
string getCopyright() const ;
UINT32 getProfileVersion() const ;
string getProfileVersionString() const ;
private :
KProfile();
KProfile(cmsHPROFILE);
KProfile( const char * , const char * );
KProfile( void * , UINT32);
cmsHPROFILE handle_;
};
}
#include " KProfileShPtr.h "
#include < lcms / lcms.h >
namespace milk
{
/* * The wrapper class of lcms's profile,
group related function together,
and use RAII idiom to manage resource. */
class PUREMILK_EXPORT KProfile : private boost::noncopyable
{
public :
~ KProfile( void );
/* * Create method, more information please refer to book
- Refactoring to Patterns. */
/* * Three basic create methods, from an existing handle,
file and memory. */
static KProfileShPtr createFromHandle(cmsHPROFILE);
static KProfileShPtr createFromFile( const char * , const char * );
static KProfileShPtr createFromMemory( void * , UINT32);
/* * Get the internal profile handle of lcms. */
cmsHPROFILE getHandle() const { return handle_;}
/* * Print out */
ostream & print(ostream & ) const ;
/* * Basic information related function. */
string getProductName() const ;
string getProductDescription() const ;
string getProductInformation() const ;
string getManufacturer() const ;
string getModel() const ;
string getCopyright() const ;
UINT32 getProfileVersion() const ;
string getProfileVersionString() const ;
private :
KProfile();
KProfile(cmsHPROFILE);
KProfile( const char * , const char * );
KProfile( void * , UINT32);
cmsHPROFILE handle_;
};
}
1. KProfile 封装了cmsHPROFILE(handle_)句柄,和许多相关的lcms API调用
2.
从boost库的noncopyable获得禁止拷贝的行为(拷贝构造函数和赋值操作符)
3. KProfile不提供public的构造函数,而是提供一系列的create methods,所有的create methods都返回KProfileShPtr(typedef boost::shared_ptr<KProfile> KProfileShPtr)
4. getHandle函数会返回内部profile句柄(资源封装类应该允许返回内部的句柄或者指针,参看Effective C++第三版)
KProfileShPtr.h
#pragma
once
namespace milk
{
/* * Shared pointer for KProfile. */
class KProfile;
typedef boost::shared_ptr < KProfile > KProfileShPtr;
}
namespace milk
{
/* * Shared pointer for KProfile. */
class KProfile;
typedef boost::shared_ptr < KProfile > KProfileShPtr;
}
1. 另外提供一个KProfileShPtr.h而不把typedef置于KProfile.h内的原因是为了提供KProfileShPtr的前置声明
(类似
C++
标准库
iosfwd
的做法),就是说当其它类的接口需要使用到KProfileShPtr的时候,它不必在头文件中包括KProfile.h,而只需要包括KProfileShPtr.h即可。(当然在所有的地方都写boost::shared_ptr<KProfile>可以免除这种麻烦,看个人喜好)
KProfile.cpp
#include
"
KProfile.h
"
#include " KCMSException.h "
namespace milk
{
KProfile::KProfile()
: handle_( 0 )
{
}
KProfile::KProfile(cmsHPROFILE handle)
: handle_(handle)
{
}
KProfile:: ~ KProfile( void )
{
if ( this -> handle_)
cmsCloseProfile( this -> handle_);
}
KProfileShPtr
KProfile::createFromHandle(cmsHPROFILE handle)
{
KProfile * profile = new KProfile(handle);
return KProfileShPtr(profile);
}
}
#include " KCMSException.h "
namespace milk
{
KProfile::KProfile()
: handle_( 0 )
{
}
KProfile::KProfile(cmsHPROFILE handle)
: handle_(handle)
{
}
KProfile:: ~ KProfile( void )
{
if ( this -> handle_)
cmsCloseProfile( this -> handle_);
}
KProfileShPtr
KProfile::createFromHandle(cmsHPROFILE handle)
{
KProfile * profile = new KProfile(handle);
return KProfileShPtr(profile);
}
}
1. 在析构函数中会释放profile句柄
2. 由于KProfile没有提供拷贝行为,所以析构函数不会担心会多次释放同一句柄,当然如果释放发生在类外部,这不是类KProfile可以控制的