RAII mechanism in C ++

Original link: http://www.cnblogs.com/diegodu/p/4689974.html

http://www.jellythink.com/archives/101

 


Foreword

In writing C ++ design patterns - Singleton time, wrote in the instance of destruction, the GC class design is very clever, and this clever design is based on the end of the life cycle when the object will automatically call its destructor function, and this clever design is also --RAII professional nouns. It will focus on the following RAII, comprehensive explanation of RAII knowledge.

What is RAII?

Resource Acquisition Is Initialization RAII is referred to, is a C ++ language resource management, to avoid leakage of usage. The principle is to use C ++ constructed object will eventually be destroyed. RAII approach is to use an object to obtain corresponding resources in its construction, in the lifetime of the object to control access to resources, so that it remains active, and finally in the object destructor when releasing resources acquired during construction.

Why use RAII?

Speaking RAII above is used to manage resources, ways to avoid resource leaks. So, with such a long time, also wrote so many programs, and resources often say verbally, then the resource is defined? In a computer system, resources are limited and have a number of effects on certain elements of the normal operation of the system. For example: network socket, mutex, and file handles memory and so on, they belong to the system resources. Because system resources are limited, like the same nature of the petroleum, iron ore, is not inexhaustible, so we use the system when programming resources, you must follow a step:

  1. Application resources;
  2. Use of resources;
  3. Free up resources.

First and second steps are indispensable, because the resources have to apply to use, after completion of use must be released, if you do not release it, it will cause resource leaks.

One of the most simple example:

#include <iostream> 

using namespace std; 

int main() 

{ 
	int *testArray = new int [10]; 
	// Here, you can use the array 
	delete [] testArray; 
	testArray = NULL ; 
	return 0; 
}

We open up new uses memory resources, and if we do not release it, it will cause a memory leak. So, when programming, new and delete operators always match operation. If you always apply resources without releasing resources will eventually lead to all the resources are occupied and no scene available resources. However, in the actual programming, we will always be careful not to put all kinds of release operation forget that it was programmed veteran, thousands of lines of code, tens of thousands of lines of code, also made such a stupid mistake .

One more example:

#include <iostream> 
using namespace std; 

bool OperationA(); 
bool OperationB(); 

int main() 
{ 
	int *testArray = new int [10]; 

	// Here, you can use the array 
	if (!OperationA()) 
	{ 
		// If the operation A failed, we should delete the memory 
		delete [] testArray; 
		testArray = NULL ; 
		return 0; 
	} 

	if (!OperationB()) 
	{ 
		// If the operation A failed, we should delete the memory 
		delete [] testArray; 
		testArray = NULL ; 
		return 0; 
	} 

	// All the operation succeed, delete the memory 
	delete [] testArray; 
	testArray = NULL ; 
	return 0; 
} 

bool OperationA() 

{ 
	// Do some operation, if the operate succeed, then return true, else return false 
	return false ; 
} 

bool OperationB() 

{ 
	// Do some operation, if the operate succeed, then return true, else return false 
	return true ; 
}

The example above model is often used in practice, we can not expect every operation is successful return, so every operation, we need to make judgments, the above example, when the operation fails, then released memory, return to the program. The above code is extremely bloated, reduced efficiency, even more frightening is that, understandability and maintainability of the program significantly reduced, when the operation increased, the code processing resources released will be more and more, more and more chaotic. If an abnormality occurs while operating a statement led to the release of resources is not called, how do? This time, RAII mechanism can come in handy.

How to use RAII?

When we use local variables within a function, when pulled out of the scope of local variables, this variable will not destroyed; when this variable is a class object, this time, it will automatically call the destructor of this class , and all this happens automatically, do not show the programmer to complete the call. This is also great, RAII is such to accomplish. Since the system resources do not have automatic release function, whereas the C ++ class with an automatic calling the destructor function. If the resource for classes encapsulate, the internal resource operations are encapsulated in the class, the resources to be released in the destructor. When the end of life of local variables defined, its destructor is called automatically, so, they do not show the programmer to call the operation to release the resource. Now, we use RAII mechanism to accomplish the above example. code show as below:

#include <the iostream> 
the using namespace STD; 

class ArrayOperation 
{ 
public: 
	ArrayOperation () 
	{ 
		m_Array = new new int [10]; 
	} 

	void initarray () 
	{ 
		for (int I = 0; I <10; I ++) 
		{ 
			* ( + I m_Array) = I; 
		} 
	} 

	void showarray () 
	{ 
		for (int I = 0; I <10; I ++) 
		{ 
			COUT << m_Array [I] << endl; 
		} 
	} 

	~ ArrayOperation () 
	{ 
		COUT < < "~ ArrayOperation iS Called" << endl; 
		IF (! m_Array = NULL) 
		{ 
			the Delete [] m_Array; // thank you very much beneficial up to a very sharp review, in detail can take up to comment on this article beneficial 2014.04.13 
			m_Array = NULL; 
		} 
	} 

Private: 
	int * m_Array; 
}; 

BOOL OperationA (); 
BOOL OperationB (); 

int main () 
{ 
	ArrayOperation arrayOp; 
	arrayOp.InitArray (); 
	arrayOp.ShowArray (); 
	return 0; 
}

The above example is not much practical significance, just to illustrate the mechanisms of RAII. Here's one example of practical importance:

/*
** FileName     : RAII
** Author       : Jelly Young
** Date         : 2013/11/24
** Description  : More information, please go to http://www.jellythink.com
*/

#include <iostream>
#include <windows.h>
#include <process.h>

using namespace std;

CRITICAL_SECTION cs;
int gGlobal = 0;

class MyLock
{
public:
	MyLock()
	{
		EnterCriticalSection(&cs);
	}

	~MyLock()
	{
		LeaveCriticalSection(&cs);
	}

private:
	MyLock( const MyLock &);
	MyLock operator =(const MyLock &);
};

void DoComplex(MyLock &lock ) // 非常感谢益可达犀利的review 2014.04.13
{
}

unsigned int __stdcall ThreadFun(PVOID pv) 
{
	MyLock lock;
	int *para = (int *) pv;

	// I need the lock to do some complex thing
	DoComplex(lock);

	for (int i = 0; i < 10; ++i)
	{
		++gGlobal;
		cout<< "Thread " <<*para<<endl;
		cout<<gGlobal<<endl;
	}
	return 0;
}

int main()
{
	InitializeCriticalSection(&cs);

	int thread1, thread2;
	thread1 = 1;
	thread2 = 2;

	HANDLE handle[2];
	handle[0] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void *)&thread1, 0, NULL );
	handle[1] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void *)&thread2, 0, NULL );
	WaitForMultipleObjects(2, handle, TRUE , INFINITE );
	return 0;
}

This example can be said to be a model of a real project, when multiple processes to access the critical variables for the wrong situation does not occur, the need for critical variables locked; the critical area of ​​the Windows example above is used to achieve lock. However, when using CRITICAL_SECTION, EnterCriticalSection and LeaveCriticalSection must be used in pairs, many times, often forget to call LeaveCriticalSection, this time a deadlock occurs phenomenon. When I would visit CRITICAL_SECTION package to MyLock class, then, I only need to define a MyLock variable without having to manually call LeaveCriticalSection display function.

Application of the above-described two examples are RAII mechanism, the understanding of the above examples, it should be understood that the use of a RAII mechanism.

Use RAII trap

When using RAII, some issues require special attention. Let me slowly come.

First example:

#include <iostream>
#include <windows.h>
#include <process.h>

using namespace std;

CRITICAL_SECTION cs;
int gGlobal = 0;

class MyLock
{
public:
	MyLock()
	{
		EnterCriticalSection(&cs);
	}

	~MyLock()
	{
		LeaveCriticalSection(&cs);
	}

private:
	//MyLock(const MyLock &);
	MyLock operator =(const MyLock &);
};

void DoComplex(MyLock lock)
{
}

unsigned int __stdcall ThreadFun(PVOID pv)  
{
	MyLock lock;
	int *para = (int *) pv;

	// I need the lock to do some complex thing
	DoComplex(lock);

	for (int i = 0; i < 10; ++i)
	{
		++gGlobal;
		cout<< "Thread " <<*para<<endl;
		cout<<gGlobal<<endl;
	}
	return 0;
}

int main()
{
	InitializeCriticalSection(&cs);

	int thread1, thread2;
	thread1 = 1;
	thread2 = 2;

	HANDLE handle[2];
	handle[0] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void*)&thread1, 0, NULL );
	handle[1] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void*)&thread2, 0, NULL );
	WaitForMultipleObjects(2, handle, TRUE , INFINITE );
	return 0;
}

This example is modified on the basis of the previous example. Add a DoComplex function, the function is called in the thread, the function is very common, however, is the argument of the function of our package of classes. You run the code, you will find, joined the function, access to global variables gGlobal whole chaos. What have you thought about, this is why? Many articles online say RAII, just to say this, but did not say why, here, my good analysis here.

Since the transfer value of the parameter used DoComplex function copies the value of this time occurs, the copy constructor calls the class to generate a temporary object, the default copy constructor is not implemented due MyLock copy constructor, it is used, then use the temporary variable in DoComplex in. When the call is completed, the temporary variable's destructor will be called, because the call LeaveCriticalSection in the destructor, led CRITICAL_SECTION left early, resulting in an access violation to gGlobal variable problem, add the following class if MyLock the code, the program can run correctly:

MyLock( const MyLock & temp ) 
{ 
    EnterCriticalSection(&cs); 
}

This is because CRITICAL_SECTION allow multiple EnterCriticalSection, however, LeaveCriticalSection EnterCriticalSection must match the phenomenon does not appear to deadlock.

To avoid falling into this trap, taking into account the package is a resource, because the resource is not available often copy semantics, therefore, in the actual implementation process, MyLock class should be as follows:

class MyLock
{
public:
	MyLock()
	{
		EnterCriticalSection(&cs);
	}

	~MyLock()
	{
		LeaveCriticalSection(&cs);
	}

private:
	MyLock(const MyLock &);
	MyLock operator =(const MyLock &);
};

This prevents the replication process resources behind, so that all resources are operating their own control them. If you want to know the copy constructor and assignment operator calls, you can properly read the "depth exploration of C ++ object model of the book."

to sum up

Having said that, the essence of RAII is to use objects that represent resources, the task management resources into the task management objects, will acquire and release the object's constructor and destructor resources corresponding to up to ensure the survival of the subject term resource is always effective, resources will be released when the object is destroyed. To put it plainly, it is to have the object, you have the resources, objects, resource then. So, RAII mechanism is a powerful weapon resource management, C ++ programmers rely RAII write code that is not only simple and elegant, but also did abnormal security. After the actual programming, you can use RAII mechanism, to make their code more beautiful.

November 24, 2013 in Dalian, Neusoft.

Reproduced in: https: //www.cnblogs.com/diegodu/p/4689974.html

Guess you like

Origin blog.csdn.net/weixin_30319097/article/details/94792226