Resources: dynamically allocated memory, file descriptors, mutex locks, fonts and brushes in the graphic interface, database connections, and network sockets, when you no longer use it, you must return it to the system.
Clause 13: object-oriented resource management
After applying for resources from the system, you must remember to release the resources. Otherwise, memory leakage may easily occur. However, it is not easy to realize such a thing. For example, we use a function to dynamically allocate memory and return a pointer.
Investment * ceateInvestment (); // return pointer, pointing to the Dynamic Allocation object void f () {Investment * pInv = ceateInvestment (); //... delete pInv ;}
Even if we call delete after using the pInv pointer like the code above, there may still be problems. Sometimes we may... in advance, return or jump out of the loop, or even an exception occurs in this part, which causes the delete operation to fail at all.
The solution is to place the pointer in a resource management class so that class objects will automatically call the Destructor at the end of their life, and delete will be executed in the destructor.
Two commonly used RAII classes are shared_ptr and unique_ptr. The difference is that shared_ptr allows multiple pointer copies in the same memory region, unique_ptr only allows one pointer to point to an object. When uniuqe_ptr assigns a value, the pointer used for the value assignment changes to null.
Clause 14: copying actions in resource management
In actual resource management, not all resources are heap. Therefore, intelligent pointers such as unique_ptr and shared_ptr are not suitable for resource managers.
For example, you need to control Mutex objects of the Mtux type. There are two functions available: lock and unlock. You need to ensure that you do not forget to unlock a locked Mutex, our idea is: resources are created during the construction and released during the analysis.
Class Lock {public: explicit Lock (Mutex * pm): mutexPtr (pm) {lock (mutexPtr;)} // obtain resources ~ Lock () {unlock (mutexPtr);} // release resources };
This is good, but if the Lock is copied, a problem will occur. It may cause a Mutex to be unlocked twice.
There are two common solutions: Prohibit replication, and sacrifice the reference notation for underlying resources. This is also the implementation principle of shared_ptr.
Clause 15: Provide access to original resources in resource management
Many API interfaces often require access to original resources. Therefore, each RAII class should provide a method to "obtain the resources managed by it", for example, providing a get function.
Access to the original resource may be explicitly or implicitly converted. In general, Explicit conversions are safer, but implicit conversions are more convenient for customers.
Clause 16: use the same form for paired use of new and delete
If you use [] in the new expression, you must use [] in the corresponding delete expression. If you do not use [] in the new expression, do not use [] in the corresponding delete expression.
String * stringPtr1 = new string; string * stringPtr2 = new string [100]; delete stringPtr1; // delete an object delete [] stringPtr2; // delete an array composed of Objects
Article 17: Insert a newed object into a smart pointer using an independent statement
Suppose we have a function to control the priority of the program, and another function is used to handle some priority on the Wieget obtained by a dynamic allocation:
int priority();void processWidget(shared_ptr<Widget>pw, int priority);
Now let's call it like this:
processWidget(share_ptr<Widget>(new Widget),priotrity());
Before calling processWidget, the compiler must create code to do the following:
- Call priority
- Execute "new Widget"
- Call the shared_ptr constructor.
The C ++ compiler does not guarantee the sequence of the last code execution, but it can be ensured that the new Widget must have occurred before the performance_ptr constructor.
If priority occurs in step 2 and an exception occurs during execution, the resource may not be released normally.
To avoid this situation, you need to use the separation statement, first create a Widget, then place it in a smart pointer, and then pass the smart pointer to processWidget.