shared

    技术2022-05-13  7

    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 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 KProfileShPtr)

    4. getHandle函数会返回内部profile句柄(资源封装类应该允许返回内部的句柄或者指针,参看Effective C++第三版)

    KProfileShPtr.h

    #pragma once  namespace milk { /** Shared pointer for KProfile. */ class KProfile;     typedef boost::shared_ptr        KProfileShPtr; }

    1. 另外提供一个KProfileShPtr.h而不把typedef置于KProfile.h内的原因是为了提供KProfileShPtr的前置声明(类似C++标准库iosfwd的做法),就是说当其它类的接口需要使用到KProfileShPtr的时候,它不必在头文件中包括KProfile.h,而只需要包括 KProfileShPtr.h即可。(当然在所有的地方都写boost::shared_ptr 可以免除这种麻烦,看个人喜好)

    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);     } }

    1. 在析构函数中会释放profile句柄

    2. 由于KProfile没有提供拷贝行为,所以析构函数不会担心会多次释放同一句柄,当然如果释放发生在类外部,这不是类KProfile可以控制的

     

    原文出处(点击此处)


    最新回复(0)