effective C++读书笔记3

目录

在资源管理中小心copy行为

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

以独立语句将newed对象置入智能指针


这是effectiveC++中第三章内容:资源管理

在资源管理中小心copy行为

不是所有资源都是heap-based,对那种资源而言,像auto_ptr和 tr1 ::shared_ ptr这样的智能指针往往不适合作为资源掌管者(resource handlers),你需要建立自己的资源管理类。

例如,假设我们使用C API函数处理类型为Mutex的互斥器对象( mutexobjects),共有lock和unlock两函数可用:

再用一个基于RALL守则的class来管理这个mutex。

例如:

​​​​​​

m12拷贝时会调用lock函数对Mutex对象进行加锁,但是m对象处于使用状态,因为其会阻塞等待,等待m1释放互斥量,但是m1此时也无法向下执行,所以这个程序就产生了死锁,所以对于我们设计的Lock这个类,我们不希望其有拷贝行为。

当RAII对象被复制时,我们通常有以下两种做法

1.禁止复制,当RAII对象被复制并不合理时。

2.对底层资源做引用计数,例如像shared_ptr。

它可以改变mutexPtr的类型,将它从 Mtex*改为tr1 ::shared ptr<Mutex>。然而很不幸tr1 : :shared_ptr的缺省行为是“当引用次数为0时删除其所指物”,我们想要的是解除锁定而不是删除。但shared_ptr是可以提供定制删除器的,也就是析构是怎样的方式去执行。

例如:get函数可以获取内部的原始指针

 再次解释:

1.当引用计数为0时,我们希望做的是解锁资源而不是删除资源,但是我们的shared_ptr提供一个“删除器”,删除器可以是一个函数或函数对象,因此我们为mutexPtr提供了unlokc函数,当引用计数为0时就会调用unlock函数,将Mutex对象解锁。
2.不再设计析构函数:因为默认的析构函数会调用其非静态成员的析构函数,因此默认的析构函数会在互斥器的引用计数为0时自动调用shared_ptr的删除器(此处为unlock函数)
 

请记住:

1.为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。为防止资源泄漏,请使用Raii对象,它们在构造函数中获得资源并在析构函数中释放资源.

2.复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为。

3.普遍而常见的 RAII class copying行为是:抑制copying、施行引用计数法(reference counting)。不过其他行为也都可能被实现。

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

使用智能指针如auto ptr或tr1 : :shared ptr保存factory函数如createInvestment的调用结果。

std::trl ::shared ptr<Investment> pInv (createInvestment());

 该createInvestment()函数会返回一个Investment指针。直接让shared_ptr去管理。

假设你希望以某个函数处理Investment对象,例如:

int daysHeld(const Investment* pi) ;

所以你要将RALL对象中的原始数据拿出来,本例为底部之Investment* ,有显式转换和隐式转换。 显式转换和隐式转换两种做法

显示转换:

tr1 : :shared ptr和 auto_ptr都提供一个get成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针(的复件)并且,tr1 : :shared_ptr和 auto_ptr也重载了指针取值(pointer dereferencing)操作符( operator->和operator*),它们允许隐式转换至底部原始指针。

看其中一种做法:

也可在自己创建的RAII类中,写一个get函数。例如:


FontHandle getFont();           //得到某种字体
void releaseFont(FontHandle fh);//释放字体
void changeFontSize(FontHandle f, int newSize); //改变字体大小
 
//FontHandle资源管理类
class Font 
{
public:
    explicit Font(FontHandle fh) :f(fh) {}
    ~Font() { releaseFont(f); }
 
    FontHandle get()const
    {
        return f;
    }
private:
    FontHandle f;
};

API可这使用:

Font f(getFont());
int newFontSize;
 
changeFontSize(f.get(), newFontSize);

另外一种方法是提供隐式转换函数

 隐式转换:

大家可以先看下这篇文章:有详细介绍隐式转换

 例如:

  operator FontHandle()const { return f; }

 后面调用函数时会比较自然点:


Font f(getFont());
int newFontSize;
 
//f会自动调用隐式转换函数转换为FontHandle,然后返回类中的FontHandle对象,将其传入参数1
changeFontSize(f, newFontSize);

但这个隐式转换也会增加错误的机会。例如:


Font f1(getFont());
FontHandle f2=f1;

例如下面将f1中的内部资源拷贝给f2,但是如果f1被销毁了,那么f2就称为“虚吊的”(dangle)

 请记住:

1.APIs往往要求访问原始资源(raw resources),所以每一个RAII class应该提供一个“取得其所管理之资源”的办法。API往往要求访问原始资源(原始资源),所以每一个Raii类应该提供一个“取得其所管理之资源”的办法。

2.对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。

以独立语句将newed对象置入智能指针

假设我们有个函数用来揭示处理程序的优先权,另一个函数用来在某动态分配所得的 widget上进行某些带有优先权的处理:

int priority();
void processwidget (std::tr1 : :shared_ptr<widget> pw,int priority);

 目前上面这条代码还不能编译通过,tr1 : :shared_ptr构造函数需要一个原始指针(raw pointer),但该构造函数是个explicit构造函数,无法进行隐式转换,将得自"newWidget"的原始指针转换为processwidget所要求的tr1 ::shared_ptr。

例如:

processWidget (std: :tr1: :shared_ _ptr<Widget> (new Widget), priority());

但上述的调用还可能出现资源泄漏的问题。原因:

编译器产出一个processwidget调用码之前,必须首先核算即将被传递的各个实参。上述第二实参只是一个单纯的对priority函数的调用,但第一实参std: :tr1 : : shared_ ptr<Widget>(new widget)由两部分组成:

C++ 编译器以什么样的次序完成这些事情呢?弹性很大。这和其他语言如Java和C#不同,那两种语言总是以特定次序完成函数参数的核算。可以确定的是"newwidget”一定执行于tr1 : :shared ptr构造函数被调用之前,但对priority的调用则可以排在第--或第二或第三执行。若先执行new语句,在执行priority,最后调用构造函数,如果priority出错了,那么资源就泄漏了。

解决方法:避免这类问题就是分离语句,将“创建的对象”与“放入智能指针对象”这两个步骤合成一步完成,而不是在函数调用中完成。

例如:


std::tr1::shared_ptr<Widget> pw(new Widget); //以单独语句存储对象
processWidget(pw, priority()); 

请记住:

1.以独立语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄漏。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值