Resource Management
The so-called resource is that once it is used, it must be returned to the system in the future! The most common resource for C + + is the dynamic allocation of memory, which if not returned, will leak memory.
1. Managing Resources with objects
We usually want to have an object to help us solve the problem of resource management (call destructors automatically), so this chapter discusses auto_ptr and shared_ptr.
Problem arises
Suppose we want to use a factory method such as:
class investment {...}; // 代表一个root classinvestment* creatinvestment() { // 返回一个指针指向继承体系内动态分配的对象。 ...}void f() { investment* pInv = createinvestment(); ... delete pInv;}
At first glance, this function does not have any problem, the normal dynamic allocation of the object, but also deleted the object. But the problem is ... There may be exceptions, such as early return, exception throw, continue of a loop, and so on, that the control flow cannot read to the DELETE statement, so there is a memory leak. So "simply because F always executes its DELETE statement" is not feasible.
Solve the problem
To solve this problem, we want to be able to put resources into objects so that the object automatically calls the destructor when it leaves its scope. So, we used the auto_ptr.
void f() { std::auto_ptr<investment> pInv(createinvestment()); ...}// 经由auto_ptr的析构函数自动删除pInv。
This simple example demonstrates two key ideas for "Managing Resources with Objects":
* Immediately after the resource is acquired, it is placed in the management object. This concept is often referred to as the "timing of resource acquisition is the time to initialize" (RAII guidelines), because we almost always get a resource after the same statement with it to initialize an object.
* Management Objects use destructors to ensure that resources are freed. No matter how the control flow leaves the scope, once the object is destroyed, its destructor is automatically called, and the resource is freed.
However, it is important to note that using auto_ptr must not allow multiple auto_ptr to point to the same object! Because each auto_ptr uses a destructor, he does not manage how many pointers point to the same object as a smart pointer. and copy-related operations will cause the original pointer to point to NULL, so that the object has only one auto_ptr point.
Based on the characteristics of auto_ptr, we can't use auto_ptr in STL containers. Auto_ptr
void f() { std::shared_ptr<investment> pInv(createinvestment()); ...}// 经由shared_ptr的析构函数自动删除pInv。
Almost like auto_ptr, but the corresponding replication behavior is much more normal.
Supplementary AUTO_PTR related knowledge
What the C + + auto_ptr do is to dynamically allocate objects and automatically perform cleanup when the object is no longer needed.
Use Std::auto_ptr to include.
The following is the source code:
Template<classT>class auto_ptr{Private: T*ap; Public://constructor & destructor-----------------------------------(1) Explicit auto_ptr(t*ptr=0)Throw(): AP (PTR) {} ~auto_ptr()Throw() {DeleteAp }//copy & Assignment--------------------------------------------(2) auto_ptr(auto_ptr& RHS)Throw(): AP (Rhs.release ()) {}Template<classY>auto_ptr(auto_ptr<Y>&RHS)Throw(): AP (Rhs.release ()) {}auto_ptr&operator=(auto_ptr&RHS)Throw() {Reset (Rhs.release ());return* This; }Template<classY>auto_ptr&operator=(auto_ptr<Y>&RHS)Throw() {Reset (Rhs.release ());return* This; }//dereference----------------------------------------------------(3)t&operator*()Const Throw() {return*ap; } t*operator()Const Throw() {returnAp }//helper functions------------------------------------------------(4) //value AccessT* get ()Const Throw() {returnAp }//release owner shipt* release ()Throw() {t* tmp (AP); AP =0;returntmp }//reset Value voidReset (t* ptr =0)Throw() {if(AP! = ptr) {DeleteAp AP = ptr; } }//special Conversions-----------------------------------------------(5) Template<classY>structauto_ptr_ref {y*yp; Auto_ptr_ref (Y*RHS): YP (RHS) {}};auto_ptr(AUTO_PTR_REF<T>RHS)Throw(): AP (RHS.YP) {}auto_ptr&operator= (AUTO_PTR_REF<T>RHS)Throw() {reset (RHS.YP);return* This; }Template<classY>operatorAuto_ptr_ref<y> ()Throw() {returnAuto_ptr_ref<y> (Release ()); }Template<classY>operator auto_ptr<Y> ()Throw() {return auto_ptr<Y> (Release ()); }};
shared_ptr
For details see: shared_ptr
Beware of copying behavior in resource management classes
We mentioned the concept of RAII as a "resource management class", and also described how auto_ptr and shared_ptr have shown this concept in heap-based resources. But not all resources are heap-based, and for that kind of resource, we need to build our own resource management classes. (stacks, which are automatically managed by the compiler, do not require manual control by the programmer; heap: Generation and release are controlled by programmers.) )
Problem arises
We handle mutex objects of type mutex and use a class to manage it.
void lock(Mutex* pm);void unlock(Mutex* pm);class Lock {public: explicit Lock(Mutex* pm) : mutexPtr(pm) { lock(mutexPtr); } ~Lock() { unlock(mutexPtr); }private: Mutex *mutexPtr;};main () { Mutex m; ... { Lock m1(&m); ... Lock m11(&m); Lock m12(m11); // 将m11复制到m12身上,会发生事? }}
What happens when a raii is copied? Here are two of your options:
* static replication . Many times it is not reasonable to allow RAII objects to be copied. It is possible to have a class like lock, because it is very rare to have a copy of a "synchronous" beginning artifact. If copying is not reasonable for RAII class, you should let the class inherit uncopyable (see previous:).
* Use the reference counting method for the underlying resource. Sometimes we want to keep resources until the last user of it is destroyed, in which case the Raii object is copied, and the reference count of the resource is incremented.
Problem solving
It is common to implement reference-counting copying behavior as long as the SHARED_PTR member variable is included. And shared_ptr allows you to specify a so-called "deleter" (a function or function object) that is called when the number of references is 0 o'clock, rather than the destructor being executed. The shared_ptr is a second parameter that is optional for the constructor.
class Lock {public: explicitLockmutexPtr(pm, unlock) { lock(mutexPtr.get// 随后会讨论get }private: shared_ptr<Mutex> mutexPtr;}
You no longer need to declare destructors here, because there is no need. Because the class destructor automatically calls the destructor of its non-static member variable, the delete function here.
Principles of resource Replication
- Copy the bottom resource . Sometimes, if you like, you can have any number of copies (replicas) for a resource. The only reason you need a "resource management class" is to make sure it is released when you no longer need a copy. In this case, copying a resource management object should also replicate the resources it includes, that is, "deep copy."
- transfer ownership of the bottom resource . In some rare occasions, you may want to make sure that only one Raii object is always pointing to an unprocessed resource, even if the Raii object is copied, the ownership of the resource is transferred from the copied to the target. Just like the meaning of Auto_ptr's reproduction.
3. Provide access to the original resource in the resource management class
APIs often require access to raw resources, so each RAII class should provide a way to "get the resources it manages".
Problem arises
In # # # We mentioned the following factory function
std::shared_ptr<investment> pInv = createinvestment();
Suppose we want to process a investment object with a function:
int daysHeld(const// 返回投资天数int days = daysHeld(pInv); // error!!! 类型不匹配!函数需要investment指针,而你传给它类型为shared_ptr的对象。
Problem solving
At this point we need a function to convert the Raii class object into its original resource. There are two ways to do this:
Show transformations
Both shared_ptr and auto_ptr provide a get function to perform the display transformation.
int days = daysHeld(pInv.get());
Similarly, like almost all smart pointers, shared_ptr and auto_ptr overload the pointer-value operator, operator, and operator*.
Implicit conversions
Provides an overloaded operator to complete an implicit conversion. Similar to the following code:
class type {public: type(inta(i) { } operatorint() { return a; }private: int a;};int main(intconstchar * argv[]) { type a(10); std::cout << a << std::endl; return0;}
Summarize
Whether an explicit conversion function should be provided to convert the Raii class to its bottom resource, or an implicit conversion should be provided, depends on the specific work that the Raii class is designed to perform, and the situation in which it is used. The best design is likely to adhere to the "make the interface easy to use correctly, not easy to misuse." It is usually better to have an explicit conversion function, such as GET, because it minimizes the possibility of a "non-intentional type conversion".
4. Use new and delete in pairs to take the same form
When you use new, two things happen: 1) The memory is allocated. 2) One or more constructors are called for this memory. Similarly, when you use Delete, two things happen: 1) One or more destructors are called for this memory. 2) memory is released. But the question comes out, how many objects are there in the memory that will be deleted? This determines how many destructors are called.
Because the memory allocations for a single object and the memory allocations for the arrays are different, we tell the compiler when we use Delete, whether we are using a single object or an array. The method is to add []!
This rule is important for people who prefer to use TypeDef, which means that the author must be clear about what the TypeDef is and what type of delete to use.
typedefstd::string AddressLines[4];std::stringnew AddressLines;delete// that is right! But user may misunderstand.
To avoid such errors, it is best not to use TypeDef in the form of arrays, but instead to replace the array with a template such as vector,string.
5. Put the new object directly into the smart pointer in a separate statement
We cannot tell what order the compiler executes a code statement, which may imply some exceptions.
Problem arises
Suppose we have a function to reveal the priority of the handler, and another function to perform some processing with precedence on a dynamically allocated widget.
// function definition:int priority();void processWidget(stdint priority);// function reference:processWidget(std::shared_ptr<Widget> (new Widget), priority());// be cautious!// shared_ptr构造函数需要一个原始指针,但该构造函数是个explicit构造函数// 无法进行隐式转换,所以需要显示的写出转换。
The problem happened very concealed!
We thought that putting an object in the Object Explorer would be an effective solution to the problem of resource leaks, but in fact there is an implicit crisis here. The problem is in the compiler run order, if the compiler is in the following order:
1. Execute the new Widget
2. Call Priority
3. Call the shared_ptr constructor
At this point, the call to priority causes an exception, and the pointer returned by the new widget is lost, causing a memory leak. (Happiness came too suddenly)
Problem solving
The solution to this problem is very simple. Now that the compiler has an opportunity to make an error because the code is running in a different order, then we force him to execute in the order we expected.
std::shared_ptr<Widget>Widget);processWidget(pw, priority());
Because the compiler does not have the freedom to rearrange "actions across statements"! So there's no problem with this code!
Therefore, it is always reasonable to put the new object directly into the smart pointer as easily as possible.
6. Differential heap and stack
- different management methods .
Stacks, which are automatically managed by the compiler without manual programmer control; heap: Generation and release are controlled by the programmer.
- The size of the space is different .
The stack has limited space, and heap memory can reach 4G.
- can produce fragments differently .
The stack is not fragmented because the stack is an advanced post-out queue. The heap is prone to fragmentation, multiple new/delete
can cause memory discontinuities, resulting in large amounts of fragmentation.
- The direction of growth is different .
The way the heap is grown is upward and the stack is downward.
- allocation method is different .
The heap is dynamically allocated. Stacks can be statically allocated and dynamically allocated, but the dynamic allocation of stacks is freed by the compiler.
- allocation efficiency is different .
The stack is the data structure provided by the machine system, and the bottom of the computer supports the stack: allocating the address of the special register storage stack, and the stack-stack has special instructions. The heap is provided by the C + + function library, the library function will follow a certain algorithm in the heap memory to search for enough space available, if there is not enough space (perhaps due to too much memory fragmentation), it is possible to call the system function to increase the memory space of the program data segment, so that there is a chance to divide enough memory, Then the return. Obviously, the heap is much less efficient than the stack.
Heap and Stack, due to the use of a large number of new/delete, prone to large amounts of memory fragmentation, due to lack of dedicated system support, inefficient, due to the possibility of triggering user-state and nuclear mentality of the switch, memory applications, the cost becomes more expensive. So the stack is the most widely used in the program, even if the call of the function is done by the stack, the parameters in the function call, the return address, the EBP and the local variables are all stored in a stack. So, we recommend that you try to use stacks instead of heaps.
Stacks are less flexible than heaps, sometimes allocating a lot of memory space, or using heaps better.
Whether it is a heap or stack, to prevent the occurrence of cross-border phenomenon.
Summarize
For C + + such a high-skilled language, resource management has never been a simple thing, so master a certain resource management method is always useful! This blog based on "effective C + +" summarizes several guidelines for resource management, with a small amount of their own additions.
[C + +] resource management