Document directory
- Iii. Resource Management
- Provide a way to access the original resources. There are two advantages to doing so:
I. What is raiI?
RaiI (resource acquisition is initialization) -- "initialization upon resource acquisition ". This is a concept of resource management, which is generally implemented through resource management. The essence is that resources are obtained in constructors and released in destructor. Resources include memory, file handle, network connection, and mutex.
Ii. Why raiI?
See the following code:
void function(){Resource* pRc = createResource();...delete pRc;}
PRC is a pointer to a resource. We wanted to initialize a resource in the function, use the resource to execute some operations, and finally release the resource. However, "delete PRC;" is not always executed. If any exception or other things occur during the operation, for example, it is silly to use the GOTO statement, or maybe this resource is in a loop, and this loop executes the continue under some circumstances. In short, as long as "delete PRC" is not executed, this resource will not be released, which is not what we are happy to see.
Of course, some may consider using the following methods to solve the exception.
void function(){try{Resource* pRc = createResource();...delete pRc;}catch(...){ delete pRc; }}
This may be a method. Considering the future maintenance, the maintenance programmer may add some code without fully understanding the function's intention to manage resources. If these codes cause exceptions that are not declared in the catch clause, the resources are still not released. Therefore, we have introduced the idea of using objects to manage resources.
Iii. Resource Management
The basic structure of resource management is not complex. A private original resource field, a constructor that can initialize the original resource, and an destructor used to release the resource. The basic idea has been mentioned above. Get Resources in the constructor and release resources in the destructor. Consider the above Code. This time we have a resource management manager.
class Manager{private:Resource* pRc ;public:Manager(Resource* pRc1);~Manager();... }public Manager();Manager::Manager(Resource* pRc1){pRc = pRc1;}Manager::~Manager(){ try{delete pRc ;}catch(...){delete pRc ;}}void function(){Manager(createResource());...}
The advantage of using manager to manage resources is that no matter how the life cycle of a function ends, the manager's destructor will always be called and the resources will be released. Of course, if an exception occurs, it will not be easy to handle. We will discuss how to solve this problem below.
Iv. Common raiI implementation
In fact, the C ++ library has provided us with a lot of smart intelligence for pointer management. For example, STD: auto_ptr.
The above function can be rewritten as follows:
void function(){std::auto_ptr<Resource> pRc(createResource());...}
When the function ends, we release the resource by using the STD: auto_ptr destructor. This is what we rely on when writing our own resource management class-releasing resources using the automatically called destructor.
However, pay attention to the following points when using STD: auto_ptr:
1. Do not use multiple STD: auto_ptr to point to the same resource. This will cause the same resource to be deleted multiple times. What will happen, you know.
2. Do not assign values or copy functions to STD: auto_ptr. Otherwise, the original one will be set to null. If you use the original STD: auto_ptr again, congratulations.
(Some auto_ptr fixes in C ++ 11)
We recommend that you use another smart pointer, rcsp (referene-counting smart pointer), to reference the counting smart pointer. The pointer behavior is a bit like Java's Reference Management (unlike C ++'s reference, the behavior is similar to the smart pointer mentioned here ), it keeps track of how many public objects are managing a resource and releases the resource when no one points to it. I have learned how to use Java or. NET to manage my shoes.
However, the world of C ++ has never had an optimal solution, but only the most suitable one. Rcsp also has its drawbacks. when it encounters a ring reference (two are not actually used but pointed to each other), it will be helpless.
Tr1: shared_ptr is an rcsp. (Note: tr1 is an extension of STD. It is called STD: tr1. If the standard library cannot meet your requirements, we recommend you look for libraries written by others on boost. This may be a surprise ). Here we rewrite the function again:
void function(){tr1:shared_ptr<Resource> pRc(createResource());...}
V. raiI simple classification
RaiI can be divided into two types based on the resource acquisition method: raiI for external initialization and raiI for internal initialization. The latter is easy to implement. STD: string is an internally initialized raiI. It encapsulates the underlying char array and the resources are invisible to external programs. The STD: auto_ptr and tr1: shared_ptr mentioned above are both externally initialized raiI. We can pass parameters to the constructor to the managed resources.
Vi. Precautions for using raiI
Pay attention to the copy behavior. The occurrence of copy behavior is usually concealed, and it is difficult to draw attention. This behavior can be implemented through the default copy constructor and copy assignment. However, for a resource management class for implementing raiI, the copy behavior is often worth noting. Copying a resource management class means that the same resource is managed by multiple resource management classes, and even the original resource itself is copied. If this is not the design you want, you must prevent such behavior. The following lists four common possibilities:
1. If you do not want the resource management class to be copied, declare the copy constructor as private.
2. When the resource management class is allowed to replicate but the resource is not expected to be copied, the "shortest copy" should be used and the "reference counting method" should be introduced to ensure that the resource is released when the last reference is destroyed.
3. The resource management class should adopt "Deep copy" When copying resources at the same time ".
4. When the resource management class is allowed to replicate but cannot direct to the same original resource, the original resource managed by the original resource management class object should be destroyed while copying.
Provide a way to access the original resources. There are two advantages to doing so:
1. If you call a third-party library, such as a certain API, the parameters required by the API function are usually the original resources, rather than the Custom Resource management class. Therefore, it is necessary to provide access to the original resources.
2. Questions about resource release of destructor. If an exception occurs during resource release, congratulations, there are usually no good methods for resource management. For example, the following code:
Class manager {PRIVATE: Resource * PRC; public: Manager (resource * prc1 );~ Manager ();...} Public Manager (); Manager: Manager () {PRC = new resource; // use the raiI format initialized internally.} MANAGER ::~ Manager () {try {Delete PRC;} catch (...) {Delete PRC ;}}
The usage of try-catch as shown above is often used in e-commerce systems. For example, in a HNA project, if the void module throws an exception to the void operation host, the void module re-performs the void operation. If the first failure to receive the ticket fails to throw an exception, the return module re-issues the ticket. Then, both the refund and the issuance failed. This design method can only prevent one failure, but cannot prevent the second failure. The design is based on two consecutive failed operations, which is a small probability event. However, in reality, a real exception often leads to a high probability of secondary operation failure, because the interval between the two operations is very small, and the exception is often not released yet. The above design can only be used to prevent occasional use of the ticketing machine. To make up for this design, e-commerce websites will introduce automatic processes. What we need to do here is to provide the customer with access to the original resources so that the customer code can directly release the resources. In this way, when an exception occurs, it is up to the customer code to determine how to handle the issue (such as release again, record logs, and notify the customer ).
This article ends now. However, writing a completely reliable raiI object is almost impossible, because when an exception occurs in resource release, we can do nothing at the code level. This is also a formal question that the C ++ world will always have to face (in fact, this is true for any programming language, and there is always something code is not enough ). Therefore, if appropriate, design your resource management class with raiI ideas.
Post-order: if the content of this article is inappropriate or incorrect, we hope the audience will point out that we can make common progress!