Objective
When writing a C + + design pattern--a single case pattern, when writing to instance destruction, the design of the GC class is very ingenious, and this ingenious design is based on when the end of the object's life cycle will automatically call its destructor, and this ingenious design is also a professional noun--raii. The following will be around Raii, a comprehensive explanation of the relevant knowledge of RAII.
What is RAII?
RAII is the abbreviation of resource acquisition is initialization, which is an idiomatic method of managing resources and avoiding leakage in C + + language. Using the principle that C + + constructs objects will eventually be destroyed. Raii's approach is to use an object, to obtain the corresponding resources during its construction, to control access to the resource during the object's lifetime, to keep it valid, and finally to release the resource acquired at the time of the object's destructor.
Why do you use RAII?
It says RAII is a way to manage resources and avoid resource leaks. So, it took so long, but also wrote so many programs, verbally often say resources, then how resources are defined? In computer systems, resources are a limited number of elements that have a certain effect on the normal operation of the system. For example: Network sockets, mutexes, file handles and memory, and so on, they belong to system resources. Because the system's resources are limited, just like the natural oil, iron ore, is not inexhaustible, so we use system resources in programming, must follow a step:
1. Application of resources;
2. Use of resources;
3. Release of resources.
The first and second steps are indispensable, because resources must be applied to use, after the use is completed, must be released, if not released, it will cause resource leaks.
One of the simplest examples:
Copy Code code as follows:
#include <iostream>
using namespace Std;
int main ()
{
int *testarray = new int [10];
Here, your can use the array
delete [] testarray;
Testarray = NULL;
return 0;
}
We use new memory resources to create a memory leak if we don't release it. So, when programming, the new and delete operations always match the operation. If you always request a resource without releasing it, you end up with a scenario in which the resource is all occupied without the resources available. However, in the actual programming, we will always be careless of the release of the operation to forget, is a veteran of programming, in thousands of lines of code, tens of thousands of lines in the code, will make this low-level error.
One more example:
Copy Code code as follows:
#include <iostream>
using namespace Std;
BOOL Operationa ();
BOOL Operationb ();
int main ()
{
int *testarray = new int [10];
Here, your 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 and else return false
return false;
}
BOOL Operationb ()
{
Do some operation, if the operate succeed, then return true and else return false
return true;
}
This example of the model, in practice is often used, we can not expect each operation is successful return, so, each operation, we need to make a judgment, in the example above, when the operation fails, then, release the memory, return the program. The above code, extremely bloated, inefficient, more frightening is, the program's understandable and maintainability significantly reduced, when the operation increased, processing resources to release the code will be more and more chaotic. What if the statement that freed the resource was not invoked when an exception occurred in an operation? This time, the RAII mechanism can come in handy.
How to use Raii?
When we use local variables inside a function, this variable is not destroyed when the scope of the local variable is exited; When the variable is a class object, this time it automatically calls the destructor of the class, and all of this happens automatically, without the programmer showing the call done. This is too good, RAII is the way to complete. Classes in C + + have the ability to invoke destructors automatically because the resources of the system do not have automatic release capabilities. If the resource is encapsulated with a class, the resource operations are encapsulated within the class, freeing the resource in the destructor. When the life of a defined local variable ends, its destructor is invoked automatically, so that the operation of the freed resource is not invoked without the programmer's display. Now, let's use the RAII mechanism to complete the above example. The code is as follows:
Copy Code code as follows:
#include <iostream>
using namespace Std;
Class Arrayoperation
{
Public:
Arrayoperation ()
{
M_array = new int [10];
}
void Initarray ()
{
for (int i = 0; i < ++i)
{
* (M_array + i) = 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)
{
Delete[] M_array; Thank you very much for the very sharp review, detailed can participate in the comments in this article 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 does not have much practical significance, just to illustrate the mechanism of the RAII problem. Here's an example of a practical significance:
Copy Code code as follows:
/*
* * Filename:raii
* * author:jelly Young
* * DATE:2013/11/24
* * Description:more information, http://www.jb51.net
*/
#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)//Thank you very much for the sharp 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 < ++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 real project model, when multiple processes access to the critical variables, in order to not appear in the wrong case, the need to lock the critical variable, the above example is the use of Windows critical area implementation of the lock. However, when using critical_section, EnterCriticalSection and leavecriticalsection must be used in pairs, often forgetting to call LeaveCriticalSection, A deadlock occurs at this time. When I encapsulate access to critical_section into the Mylock class, I just need to define a Mylock variable instead of having to manually show the calling LeaveCriticalSection function.
The above two examples are the RAII mechanism application, understands the above example, should be able to understand the RAII mechanism use.
Using the Raii Trap
Some problems require special attention when using RAII. Let me go slowly.
Let me give you an example:
Copy Code code as follows:
#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 < ++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. Added a Docomplex function, called in a thread, that function is normal, but the parameter of the function is the class we encapsulate. You run the code and you find that the access to the Gglobal global variable is all messed up by the addition of the function. Do you have any thought, this is why? Many of the online raii articles, all just said the question, but did not say why, here, I have a good analysis here.
Because the parameters of the Docomplex function use the pass value, a value is replicated, the copy constructor of the class is invoked, a temporary object is generated, and because Mylock does not implement the copy constructor, the default copy constructor is used. The temporary variable is then used in the Docomplex. When the call completes, the destructor of the temporary variable is invoked, because the leavecriticalsection is invoked in the destructor, causing the critical_section to be left in advance, resulting in a violation of the Gglobal variable access conflict. If you add the following code to the Mylock class, the program will run correctly:
Copy Code code as follows:
MyLock (const MyLock & temp)
{
EnterCriticalSection (&CS);
}
This is because critical_section allows multiple entercriticalsection, but leavecriticalsection must match entercriticalsection to avoid deadlocks.
In order to avoid falling into this trap, and considering that the encapsulation is a resource, because the resources are often not copy semantics, so in the actual implementation process, the Mylock class should be as follows:
Copy Code code as follows:
Class MyLock
{
Public
MyLock ()
{
EnterCriticalSection (&CS);
}
~mylock ()
{
LeaveCriticalSection (&CS);
}
Private
MyLock (const MyLock &);
MyLock operator = (const MyLock &);
};
This prevents the resources behind the process of replication, so that all the operations of resources in their own control. If you want to know the copy constructor and assignment operator calls, you can read the Deep exploration of the C + + object model of the book.
Summarize
So much has been said, the essential content of RAII is to represent resources with objects, to transform the task of managing resources into tasks of managing objects, to match the acquisition and release of resources with the construction and deconstruction of objects, so as to ensure that resources are always valid during the lifetime of objects, and that resources will be released when objects are destroyed. To be blunt, you have the object, you have the resources, the object, the resources. Therefore, the RAII mechanism is a powerful weapon for resource management, and C + + programmers rely on RAII to write code that is not only simple and elegant, but also exceptionally safe. In future programming practice, you can use the RAII mechanism to make your code more beautiful.