分享
 
 
 

C++箴言:谨慎考虑资源管理类的拷贝行为

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

在上一篇文章中介绍了作为资源治理类支柱的 Resource Acquisition Is Initialization (RAII) 原则,并描述了 auto_ptr 和 tr1::shared_ptr 在基于堆的资源上运用这一原则的表现。并非所有的资源都是基于堆的,然而,对于这样的资源,像 auto_ptr 和 tr1::shared_ptr 这样的智能指针通常就不像 resource handlers(资源治理者)那样合适。在这种情况下,有时,你可能要根据你自己的需要去创建你自己的资源治理类。

例如,假设你使用 C API 提供的 lock 和 unlock 函数去操纵 Mutex 类型的互斥体对象:

void lock(Mutex *pm); // lock mutex pointed to by pm

void unlock(Mutex *pm); // unlock the mutex

为了确保你从不会忘记解锁一个被你加了锁的 Mutex,你希望创建一个类来治理锁。RAII 原则规定了这样一个类的基本结构,通过构造函数获取资源并通过析构函数释放它:

class Lock {

public:

eXPlicit Lock(Mutex *pm)

: mutexPtr(pm)

{ lock(mutexPtr); } // acquire resource

~Lock() { unlock(mutexPtr); } // release resource

private:

Mutex *mutexPtr;

};

客户按照 RAII 风格的惯例来使用 Lock:

Mutex m; // define the mutex you need to use

...

{ // create block to define critical section

Lock ml(&m); // lock the mutex

... // perform critical section operations

} // automatically unlock mutex at end

// of block

这没什么问题,但是假如一个 Lock 对象被拷贝应该发生什么?

Lock ml1(&m); // lock m

Lock ml2(ml1); // copy ml1 to ml2-what should

// happen here?

这是一个更一般问题的特定实例,每一个 RAII 类的作者都要面临这样的问题:当一个 RAII 对象被拷贝的时候应该发生什么?大多数情况下,你可以从下面各种可能性中挑选一个:

禁止拷贝。在很多情况下,答应 RAII 被拷贝是没有意义的。这对于像 Lock 这样类很可能是正确的,因为同步的基本要素的“副本”很少有什么意义。当拷贝对一个 RAII 类没有什么意义的时候,你应该禁止它。Item 6 解释了如何做到这一点。声明拷贝操作为私有。对于 Lock,看起来也许像这样:

class Lock: private Uncopyable { // prohibit copying - see

public: // Item 6

... // as before

};

对底层的资源引用计数。有时人们需要的是保持一个资源直到最后一个使用它的对象被销毁。在这种情况下,拷贝一个 RAII 对象应该增加引用这一资源的对象的数目。这也就是使用 tr1::shared_ptr 时“拷贝”的含意。

通常,RAII 类只需要包含一个 tr1::shared_ptr 数据成员就能够实现引用计数的拷贝行为。例如,假如 Lock 要使用引用计数,他可能要将 mutexPtr 的类型从 Mutex* 改变为 tr1::shared_ptr<Mutex>。不幸的是,tr1::shared_ptr 的缺省行为是当它所指向的东西的引用计数变为 0 的时候将它删除,但这不是我们要的。当我们使用 Mutex 完毕后,我们想要将它解锁,而不是将它删除。

幸运的是,tr1::shared_ptr 答应一个 "deleter" 规范——当引用计数变为 0 时调用的一个函数或者函数对象。(这一功能是 auto_ptr 所没有的,auto_ptr 总是删除它的指针。)deleter 是 tr1::shared_ptr 的构造函数的可选的第二个参数,所以,代码看起来就像这样:

class Lock {

public:

explicit Lock(Mutex *pm) // init shared_ptr with the Mutex

: mutexPtr(pm, unlock) // to point to and the unlock func

{ // as the deleter

lock(mutexPtr.get()); // see Item 15 for info on "get"

}

private:

std::tr1::shared_ptr<Mutex> mutexPtr; // use shared_ptr

}; // instead of raw pointer

在这个例子中,注重 Lock 类是如何不再声明一个析构函数的。那是因为它不再需要。Item 5 解释了一个类的析构函数(无论它是编译器生成还是用户定义)会自动调用这个类的非静态(non-static)数据成员的析构函数。在本例中,就是 mutexPtr。但是,当互斥体的引用计数变为 0 时,mutexPtr 的析构函数会自动调用的是 tr1::shared_ptr 的 deleter ——在此就是 unlock。(看过这个类的源代码的人多半意识到需要增加一条注释表明你并非忘记了析构,而只是依靠编译器生成的缺省行为。)

拷贝底层的资源。有时就像你所希望的你可以拥有一个资源的多个副本,唯一的前提是你需要一个资源治理类确保当你使用完它之后,每一副本都会被释放。在这种情况下,拷贝一个资源治理对象也要同时拷贝被它隐藏的资源。也就是说,拷贝一个资源治理类需要完成一次“深层拷贝”。

某些标准 string 类型的实现是由堆内存的指针组成,堆内存中存储着组成那个 string 的字符。这样的字符串对象包含指向堆内存的指针。当一个 string 对象被拷贝,这个副本应该由那个指针和它所指向的内存组成。这样的 strings 表现为深层拷贝。

传递底层资源的所有权。在某些非凡场合,你可能希望确保只有一个 RAII 对象引用一个未加工的资源,而当这个 RAII 对象被拷贝的时候,资源的所有权从被拷贝的对象传递到拷贝对象。就像上一篇文章所说明的,这就是使用 auto_ptr 时“拷贝”的含意。

拷贝函数(copying functions)(拷贝构造函数和拷贝赋值运算符)可能是由编译器生成的,所以除非编译器生成的版本所做的事正是你所要的,你应该自己编写它们。在某些情况下,你也要支持这些函数的泛型化版本。

Things to Remember

·拷贝一个 RAII 对象必须拷贝它所治理的资源,所以资源的拷贝行为决定了 RAII 对象的拷贝行为。

·普通的 RAII 类的拷贝行为不接受拷贝和进行引用计数,但是其它行为是有可能的。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有