C++在资源管理类中提供对原始资源的访问

简介: C++在资源管理类中提供对原始资源的访问

1.问题的引入


在一个完美的世界里,资源管理类会帮你完成对资源的所有操作,自己不用关心资源管理类里面的原始资源。但现实是残酷的,有时候仍然需要自己直接接触资源管理类所封装的原始资源。


在之前的文章C++中基于对象来管理资源我们使用智能指针shared_ptr保存工厂函数createInvestment()的调用结果,如下所示:


1std::shared_ptr<Investment> pInv(createInvestment());


假设现在你希望用某个函数daysHeld()去处理Investment对象,如果你想像下面那样调用它,就会编译不通过。因为daysHeld()函数需要的参数是Investment*指针,但你传给它的参数却是shared_ptr<Investment>类型的对象。如下所示:


1int daysHeld(const Investment* pi);
2int days = daysHeld(pInv);


2.解决方法



为了解决上面的问题,你需要一个函数去将RAII类的对象(shared_ptr)转换为其内部所含有的原始资源(Investment*)有两个方法可以完成目标:显式转换和隐式转换。


方法1:显式转换


shared_ptr和auto_ptr都提供了一个get成员函数,用来执行显示转换。即它会返回智能指针内部的原始指针。如下所示:


1int days = daysHeld(pInv.get());


方法2:隐式转换


像所有的智能指针一样,shared_ptr和auto_ptr也重载了指针的解引用运算符(->和*),它们允许隐式转换底层原始指针。


1class Investment{
 2public:
 3    bool isTaxFree() const;
 4    // ...
 5
 6};
 7
 8Investment* createInvestment();  // 工厂函数用来返回指向Investment对象的指针
 9
10std::shared_ptr<Investment> pi1(createInvestment());    // 令shared_ptr管理一笔资源
11bool taxable1 = !(pi1->isTaxFree());   // 经由->去访问资源
12// ...
13
14std::auto_ptr<Investment> pi2(createInvestment());  // 令auto_ptr管理一笔资源
15bool taxable2 = !((*pi2).isTaxFree());  // 经*去访问资源


3.其他情形



有时候,我们需要把RAII资源管理类的对象所封装的原始资源拿出来,可以定义一个隐式转换函数,将资源管理类隐式或显式转换为原始资源。例如,要实现对C API中的字体类型(font)的资源管理。

1FontHandle getFont();   // C API定义的分配字体函数
2void releaseFont(FontHandle fh);  // C API定义的释放字体函数


下面,我们定义自己的RAII资源管理类Font,如下所示:


1class FontHandle{
 2    // ...
 3};
 4
 5FontHandle getFont();   // C API定义的分配字体函数
 6void releaseFont(FontHandle fh);  // C API定义的释放字体函数
 7
 8class Font{
 9public:
10    explicit Font(FontHandle fh): f(fh)   // C只能使用值传递
11    { 
12        // 构造时获取资源
13    }
14    ~Font(){
15        releaseFont(f);
16    }
17private:
18    FontHandle f;
19};


如果我们要使用某些C API,只能通过使用FontHandle类型。因此,就需要把Font类型显式转换为FontHandle类型,于是定义一个显式转换的函数get()。


1class Font{
 2public:
 3    explicit Font(FontHandle fh): f(fh)   // C只能使用值传递
 4    { 
 5        // 构造时获取资源
 6    }
 7    FontHandle get() const {return f;}  // 显示转换函数
 8    ~Font(){
 9        releaseFont(f);
10    }
11private:
12    FontHandle f;
13};


但是,上面这样的显式转换的一个缺点是每次使用都要调用get()函数,比较麻烦。如下所示:


1void changeFontSize(FontHandle f, int newSize);  // C API
2Font f(getFont());
3int newFontSize;
4// ...
5changeFontSize(f.get(), newFontSize);


另一个缺点是:既然我们写了RAII资源管理类,为什么还要每次只使用它的原始资源,这与我们希望避免资源泄漏的初衷背道而驰。因此,下面来看看隐式转换


1class Font{
 2public:
 3    explicit Font(FontHandle fh): f(fh)   // C只能使用值传递
 4    { 
 5        // 构造时获取资源
 6    }
 7    operator FontHandle() const {  // 隐式转换函数
 8        return f;
 9    }
10    ~Font(){
11        releaseFont(f);
12    }
13private:
14    FontHandle f;
15};


隐式转换会使得客户调用C API时会方便很多。如下所示:


1Font f(getFont());
2int newFontSize;
3// ...
4changeFontSize(f, newFontSize); // 隐式转换


但是,隐式转换也会增加发生错误的机会,某些类型错误就不会被编译器探测到。原本希望把一个Font对象拷贝进另一个Font对象,但如果某个人误把打Font打成了FontHandle,这样就把我们的资源管理对象变成了原始资源对象,编译器也不会报错。


1Font f1(getFont());
2
3// ...
4FontHandle f2 = f1;


结果:程序有一个FontHandle对象被f1封装和管理,但这个FontHandle通过上面的操作被拷贝进了f2,这样f1和f2同时控制着一个FontHandle。如果f1被释放,这个FontHandle也将被释放,就会导致f2的字体被损坏。


对于使用隐式转换还是显式转换要根据不同的需求来决定,如果想保证代码的正确性,显式转换的get()函数可能是更好的选择;如果想代码自然易懂,隐式转换可能更好,两者各有优缺点。


4.总结


(1) API通常需要使用原始资源作为参数,因此我们的RAII资源管理类要保证它所封装的资源是对外界可接触的。(2) 对原始资源的访问可以通过显式转换或隐式转换。一般来说,显式转换比较安全,但隐式转换对客户使用比较方便。

相关文章
|
5天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
15 4
|
5天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
15 4
|
5天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
10 1
|
15天前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
15天前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
6天前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
13 0
|
11天前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
2月前
|
机器学习/深度学习 数据采集 算法框架/工具
使用Python实现深度学习模型:智能人力资源管理与招聘
【8月更文挑战第12天】 使用Python实现深度学习模型:智能人力资源管理与招聘
82 2
|
3月前
|
数据采集 监控 数据安全/隐私保护
ERP系统中的人力资源管理与员工绩效评估解析
【7月更文挑战第25天】 ERP系统中的人力资源管理与员工绩效评估解析
206 1
|
3月前
|
机器学习/深度学习 Oracle 安全
人力资源管理革新:6款系统一站式解决HR事务
**Zoho People、BambooHR、Workday、ADP Workforce Now和Oracle HCM Cloud是知名的人力资源管理系统。Zoho People提供模块化设计、移动应用和自动化工作流;BambooHR以用户友好界面和员工档案管理见长;Workday侧重全球化云解决方案和智能决策工具;ADP Workforce Now集成HR与薪资管理,强调合规性;Oracle HCM Cloud则以高度定制和分析工具闻名。这些系统各有特点,适用于不同规模和需求的企业。**
79 11