Resource Acquisition is Initialization (RAII)

1. Example of Mutex_lock()

1.1 Bad Performance

Mutex_t mu = MUTEX_INITIALIZER();

void functionA(){
	Mutex_lock(&mu);
	... // Do a bunch of things
	Mutex_unlock(&mu);	// Bad performacne. We can't guarentee this line will always be executed.
}

1.2 Good Performance

class Lock{
private:
	Mutext_t *m_pm;
public:
	explicit Lock(Mutex_t *pm){
		Mutex_lock(pm);
		m_pm = pm;
	}
	~Lock(){ Mutex_unlock(m_pm); }
}

void functionA() {
	Lock mylock(&mu);
	... // Do a bunch of things
	// The mutex will always be released when mylock is destroied from stack.
}

Conclusion: The only way that can guarantee the code to be executed after exception is thrown are the destructor of the objects residing in the stack.

Resource management therefore needs to be tied to the lifespan of suitable objects in order to gain automatic deallocation and reclamation.

2. Example of std::share_ptr

int functionA(){
	std::shared_ptr<dog> pd(new dog());
	...
}
// The dog is destructed when pd goes out of scope (no more pointer points to pd)

Question: What is the problem with the following code?

// Another example:
class dog;
class Trick;
void train(std::shared_ptr<dog> pd, Trick dogtrick);
Trick getTrick();

int main() {
	train(std::shared_ptr<dog>(new dog()), getTrick());
}

What happens in train()'s parameter passing?

  1. new dog()
  2. getTrick()
  3. construct std::shared_ptr<dog>

The order of these operations are determined by compiler. If compiler performs the operations in the above order, we many have an issue.

If getTrick() throws an exception, then the construction of shared_ptr<dog> fails, but since now we already have an new dog() which means we will have memory leakage.

Conclusion: Don’t combine the operation of storing objects in a shared pointer with any other statement. In other words, put the storing objects in shared pointer statement to a standalone statement. So in this example, we should write like that

int main() {
	std::shared_ptr<dog> pd(new dog());
	train(pd, getTrick())
}

What happens when resource Management Object is copied

// construct L1 by mu and copy L1 and construct L2 
Lock L1(&mu);
Lock L2(L1);

Usually the mutex is mutually exclusive so it can’t be shared by multiple clients.

Solution1: Prohibit copying. Disallow copy constructor and copy assignment operator from being used.

Solution 2: Allow the resource to be shared and guarantee the resources will be released appropriately when all the clients are done with it. Reference-count the underlying resource by using std::shared_ptr

// Constructor of shared_ptr can take the second parameter: a deleter
// a deleter is a function that will be invoked when the shared pointer is destroyed.
template<class Other, class D>
shared_ptr(Other *ptr, D deleter);

// The dafault value for the deleter is operator "delete"
std::shared_ptr<dog> pd(new dog());

class Lock {
private:
	std::shared_ptr<Mutex_t> pMutex;
public:
	explicit Lock(Mutex_t *pm) : pMutex(pm, Mutex_unlock) {
		Mutex_lock(pm);
		// The second parameter of shared_ptr constructor is "deleter" function.
	}
}

int main() {
	Lock L1(&mu);
	Lock L2(L1);
}

Explanation: Lock L1(&mu); The Lock class get a mutex pointer and use it along with its unlock function to construct a shared pointer. Then in the function body it locks the mutex.
Lock L2(L1); create another pMutex pointer point to the pm, if we use L2.pMutex.use_count() we will get 2.
The Mutex_unlock() will only be invoked when no pointer pointing to pm.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值