第三章:资源管理

以对象管理资源

中心思想:把资源放进对象内,便可以依赖C++的析构函数自动调用机制确保资源被释放。

关键点:

  1. 获得资源后立刻放进管理对象。(Resource Acquisition Is Initialization,RAII

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

class Lock
{
public:
    explict Lock(Mutex* pm)
    : mutexPtr(pm)
    {
        lock(mutexPtr);
    }
    ~Lock()
    {
        unlock(mutexPtr);
    }
private:
    Mutex *mutexPtr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

注意事项:

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

  2. 两个常被使用的RAII classes分别是share_ptr和auto_ptr,前者通常是较佳选择。

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

如果RAII对象被复制,会发生什么情况?

禁止复制

通常允许RAII对象被复制不合理,如果需要禁止copy RAII对象,可以通过将copying操作声明为private(继承Uncopyable类)来禁止copy;

对底层资源使用引用计数法(reference-count)

如果需要保留资源直到资源的最后一个使用者被销毁,可实现引用计数法(shared_ptr)。

class Lock
{
public:
    explict Lock(Mutex* pm)
    : mutexPtr(pm , unlock)
    {
        lock(mutexPtr.get());
    }
    ~Lock()
    {
        unlock(mutexPtr);
    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

std::tr1::shared_ptr允许指定删除器,当引用次数为0时调用。

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

#include <iostream>
#include <memory>

using namespace std;

class MyTest
{
public:
	MyTest()
	:n(0)
	{
		cout<<"MyTest()"<<endl;
	}
	~MyTest()
	{
		cout<<"~MyTest()"<<endl;
	}
	MyTest(const MyTest& m)
	{
		cout<<"copying MyTest"<<endl;
		this->n = m.n;
	}
	void printMyTest(MyTest* m)
	{
		cout<<"printMyTest "<<m->n<<endl;
	}
private:
	int n;
};

int main()
{
	//std::shared_ptr<MyTest> m_test = new MyTest();
	//error: conversion from ‘MyTest*’ to non-scalar type ‘std::shared_ptr<MyTest>’ requested

	std::shared_ptr<MyTest> m_test(new MyTest());
	m_test->printMyTest(m_test.get());
	
    //m_test->printMyTest(m_test);
	//error: cannot convert ‘std::shared_ptr<MyTest>’ to ‘MyTest*’ ,通过get方法获取raw指针
	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

执行结果:

[root@192 15]# ./main
MyTest()
printMyTest 0
~MyTest()
1
2
3
4

成对使用new和delete时要采用相同形式

被删除的指针对象,是单一对象或是对象数组,处理方式不同,对象数据内存布局里会多存储一个记录n,表示数组的大小。

(备注:不要对数组形式做typedef操作,容易产生误操作)

std::string* mystr = new std::string();
std::string* mystrArr = new std::string[100];

delete mystr;
delete [] mystrArr;


typedef std::string strArr[10];
std::string* str = new strArr;

delete str;//出错
delete [] str;//正确
1
2
3
4
5
6
7
8
9
10
11
12

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

processWidget(std::shared_ptr<Widget>(new Widget), priority());
1

调用processWidget需要经历以下三个步骤:

  1. 调用priority()函数;
  2. 执行 new Widget
  3. 调用std::shared_ptr构造函数

C++编译器以什么样的次序完成上述三个步骤存在弹性。如果执行顺序是213,然后在调用priority的过程中出现异常,那new Widget返回的指针将会遗失,不会存入std::shared_ptr内。 即资源创建和资源被转换为资源管理对象过程中,可能发生异常干扰。因此建议将上述函数调用改为:

std::shared_ptr<Widget> pw(new Widget)
processWidget(pw,priority());
1
2