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) 对原始资源的访问可以通过显式转换或隐式转换。一般来说,显式转换比较安全,但隐式转换对客户使用比较方便。

相关文章
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4
|
30天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
30天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
30天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
16 0
|
1月前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
3月前
|
机器学习/深度学习 数据采集 算法框架/工具
使用Python实现深度学习模型:智能人力资源管理与招聘
【8月更文挑战第12天】 使用Python实现深度学习模型:智能人力资源管理与招聘
105 2