【Effective C++】资源管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/daaikuaichuan/article/details/85143373

一、以对象管理资源

1、RAII和智能指针

// 返回指针,指向Investment继承体系内的动态分配对象,调用者有责任删除它
Investment* createInvestment();   

void func()
{
    Investment* pInv = createInvestment();   //调用factory函数
    /....../
    delete pInv;      //释放pInv所指对象
}

  上述代码可能出现如下问题导致无法删除pInv指针所指对象,出现资源泄露

  • “…”区域内一个过早结束的return语句;

  • delete动作位于某个循环内,而该循环由于某个continue或goto语句过早结束;

  • “…”区域内语句抛出异常。

【解决方法】:

  • 获得资源后立刻放进管理对象内。实际上,以对象管理资源”的观念常被称为“资源取得时机便是初始化时机”(Resource Acquisition Is Initialization;RAII每一笔资源都在获得的同时立刻被放进管理对象中。

  • 管理对象运用析构函数确保资源被释放。

void func()
{
    /....../
    shared_ptr<Investment> pInv(createInvestment());
    /....../ // shared_ptr的析构函数会自动删除pInv
}

2、请记住

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

  • 常被使用的RAII classes分别是shared_ptr、weak_ptr和unique_ptr。


二、在资源管理类中小心copying行为

1、RAII对象被复制时应该怎么做?

  • 禁止复制。

  将copying函数声明为private或者使用**=delete**。

  • 对管理资源使用引用计数法。

  shared_ptr便是如此。可将mutexPtr类型从Mutex* 改为shared_ptr。当然mutex也有自己的资源管理器:lock_guard

  • 复制底部资源。

  也就是说,复制资源管理对象是,进行的是”深度拷贝“。

  • 转移底层资源的拥有权。

  有时候资源的拥有权只能给一个对象,这时候当资源复制时,就需要剥夺原RAII类对该资源的拥有权。像unique_ptr。在C++11新标准中的std::move便是这个功能。可以把一个左值转换为一个右值。

2、请记住

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

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


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

1、如何操纵原始资源

  前面两节都在讨论如何管理资源,一般情况下,使用资源管理类来屏蔽原始资源,对抗内存泄露等问题,避免使用原始资源。这样我们就无法直接访问原本的原始资源。毕竟程序在有些时候是需要操纵原始资源的,许多APIs要求使用原始资源。为了能操纵原始资源,我们要怎么做? shared_ptr提供了一个get函数,用于执行这样的显示转换。这时如果在调用API时,如下:

int daysHeld(const Investment* pi)
{
	/....../
}

shared_ptr<Investment> pInv(createInvestment);

int days = daysHeld(pInv); //通不过编译,因为实参不是Investment类型

int days = daysHeld(pInv.get()); //正确,通过成员函数get()获取原始资源的访问。

2、请记住

  • APIs往往要求访问原始资源,所以每一个RAII class应该提供一个“取得其所管理之资源”的办法。

  • 对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换(提供一个显式转换函数,如get)比较安全,但隐式转换(类中重写“()”运算符)对客户比较方便。


四、成对使用new和delete要采用相同的格式

1、请记住

  • 如果你在new表达式中使用[], 必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。使用new和delete一定要采用相同的方式。

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

1、资源泄漏的问题

  此条款是为了保证不发生资源泄漏,此处举一实例更好说明问题。

int priority();

void processWidget(shared_ptr<Widget> pw, int priority);

processWidget(shared_ptr<Widget>(new Widget), priority());

调用processWidget之前,编译器必须创建代码,做以下三件事:

1. 调用priority 或 执行“new Widget”;

2. 执行“new Widget” 或 调用priority;

3. 调用shared_ptr构造函数 或 调用shared_ptr构造函数。

  如果在执行第三行代码时,调用priority发生异常,此时可能属上面左边情况,new Widget返回的指针将会遗失从而发生资源泄漏。于是正确的做法:

// 先创建对象再调用
shared_ptr<Widget> pw(new Widget);// 在单独语句内以智能指针存储newed所得对象
processWidget(pw, priority());// 此时调用动作绝不至于造成内存泄漏

2、请记住

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

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/85143373