C ++ memory management learning notes (5)

Source: Internet
Author: User

/*************************************** *************************/

Previous content review:

C ++ memory management learning notes (4)

2.1-2.2 RAII rule (introduced) 2.3 smart pointer 2.4 auto_ptr class


--------------------------------------------------------------------------------

2.5 Resource Transfer
Resource Transfer is a secure Transfer of resources across different scopes. This problem becomes obvious when you process containers. You can dynamically create a string of objects, store them in a container, take them out, and finally arrange them. In order to make this secure job -- not leaked -- the object needs to change its owner.

One obvious solution to this problem is to use Smart Pointer, either before or after they are added to the container. This is how he works. You add the Release method to the Smart Pointer:

1: template <class T> 2: T * SmartPointer <T>: Release () 3: {4: T * pTmp = _ p; 5: _ p = 0; 6: return pTmp; 7 :}

Note that after Release is called, Smart Pointer is no longer the object owner-its internal Pointer Points to null. Now, all called Release must be a responsible person and quickly hide the returned pointer to the new owner object. In our example, the container calls Release, for example, the Stack example:

1: void Stack: Push (SmartPointer <Item> & item) throw (char *) 2: {3: if (_ top = maxStack) 4: throw "Stack overflow"; 5: _ arr [_ top ++] = item. release (); 6 :};
Similarly, you can use Release to enhance the reliability of your code.

For details about this part, refer to the auto_ptr smart pointer in "C ++ memory management learning notes (3)". The auto_ptr object changes the object owner by assigning values, copying, and reset operations.

2.6 share ownership
Finding or specifying an owner for resources in each program is the best way to share ownership.

The shared responsibility is assigned to the shared object and its client ). A shared resource must maintain a reference count for its owner. On the other hand, the owner must notify the shared object when releasing resources again. The last release of resources is responsible for free at the end.

Example: The simplest way to share is to share an object that inherits the reference Count class RefCounted:

1: class RefCounted 2: {3: public: 4: RefCounted (): _ count (1) {}5: int GetRefCount () const {return _ count;} 6: void IncRefCount () {_ count ++;} 7: int DecRefCount () {return -- _ count;} 8: private: 9: int _ count; 10 :};
By resource management, a reference count is a resource. If you keep it, you need to release it. When you realize this fact, the rest becomes simple. Simple rule-re-constructor obtains the reference count and releases it in the destructor.

As mentioned in the previous study note (3), smart pointers can be set to transfer ownership or use reference counting methods. For these two solutions, there are two types of smart pointers. auto_ptr in STL belongs to the ownership transfer pointer, and shared_ptr in boost belongs to the reference counting type (there are 6 smart pointers in boost, scoped_ptr, scoped_array, shared_array, intrusive_ptr, weak_ptr ).

Q: STL and boost?
1. STL
The standard library provides basic facilities for C ++ programs. Although the C ++ standard library has been tossing around with the C ++ standard for many years and has not been finalized until the standard is introduced, it is very gratifying to see a variety of implementations in the implementation of the standard library, it has been proven to be an industrial-level masterpiece.
STL has two main features: Data Structure and algorithm separation, which are not object-oriented. The access object is implemented through an iterator like a pointer; the container is a data structure such as a linked list or vector and provided in template mode; the algorithm is a function template, used to operate on data in a container. Because STL is based on templates, it can be used for any data type and structure.
(1) STL is the separation of data structures and algorithms. Although this is a simple concept, this separation does make STL very common. For example, because STL's sort () function is completely universal, you can use it to operate almost any data set, including linked lists, containers, and arrays.
(2) STL is not object-oriented. To be universally available, STL mainly depends on templates rather than encapsulation. Inheritance and virtual functions (polymorphism) are three elements of OOP. You cannot find any obvious class inheritance relationships in STL. This seems to be a kind of regression, but this is exactly the underlying feature that makes STL components have a wide range of versatility. In addition, because STL is based on templates, the use of inline functions makes the generated code short and efficient.
2. boost
The Boost library is a C ++ library that has been tested, transplanted, and provided with source code. As a backup for the standard library, it is one of the engines of the C ++ standardization process. The Boost library was initiated by members of the C ++ Standards Committee working group. It has a significant impact in the C ++ community and has nearly 2000 members. The Boost Library provides us with the latest, coolest, and most practical technologies. It is an uncompromising standard library.

There are several well-known libraries in Boost:

Regex: Regular Expression Library;

Spirit LL parser framework, which expresses EBNF directly using C ++ code

Graph: Graph components and algorithms;

Lambda: defines short and anonymous function objects in the called place. It is a very practical functional function.

Concept check: check concept in generic programming

Mpl: meta-programming framework implemented by templates

Thread: Portable C ++ multi-Thread Library

Python: ing C ++ classes and functions to Python

Pool: Memory Pool Management

Smart_ptr

In general, Boost is a database of high practical value and quality. In addition, the emphasis on cross-platform and Standard C ++ is an essential tool for developers of modern C ++. However, there are many experimental things in Boost, so you need to be cautious when using it in actual development. In addition, many library functions in Boost can be called extensions of language functions. They are constructed in a sophisticated manner and do not rashly spend time studying them. On the other side of Boost, libraries such as Graph are excellent code with industrial strength, good structure, and worth studying. They can also be used in product code.

Differences:

Boost is a quasi-standard library, which is equivalent to the continuation and expansion of STL. Its Design Concept is similar to that of STL, and it uses generics to maximize reuse. However, boost is more practical than STL. STL is concentrated in the algorithm part, while boost contains many tool classes to complete specific work.
 

Next, we will explain share_ptr, which is a smart pointer that can share ownership.

2.7 pai_ptr
(1) smart pointer in boost

Boost provides the following Smart Pointers to boost your code ):

 


Put the original text part up to prevent the author's translation level from being limited and affecting reading. Please refer to the following content:

Pai_ptr <T> uses a reference counter to determine whether the pointer needs to be released. It is the most common smart pointer in boost.
Scope_ptr <T> the pointer is automatically released when the scope of the pointer disappears. The performance is similar to that of the built-in pointer.
Intrusive_ptr <T> also maintains a reference counter, which has better performance than shared_ptr. However, T is required to provide this reference counting mechanism.
Weak_ptr <T> weak pointer. It must be used with shared_ptr to avoid circular reference.
Performance_array <T> is similar to shared_ptr, But It accesses arrays.
Scope_array <T> is similar to scoped_ptr, But It accesses an array.

(2) Introduction of pai_ptr

First, let's take an example to understand this smart pointer,

1: void Sample_Shared () 2: {3: // (A) create a new CSample instance with one reference 4: boost: shared_ptr <CSample> mySample (new CSample ); 5: printf ("The Sample now has % I references \ n", mySample. use_count (); // shocould be 1 6: 7: // (B) assign a second pointer to it: 8: boost: shared_ptr <CSample> mySample2 = mySample; // shocould be 2 refs by now 9: printf ("The Sample now has % I references \ n", mySample. use_count (); 10: 11: // (C) set the first pointer to NULL 12: mySample. reset (); 13: printf ("The Sample now has % I references \ n", mySample2.use _ count (); // 1 14: 15: // the object allocated in (1) is deleted automatically 16: // when mySample2 goes out of scope 17 :}

In code block (A), create A CSample object in the heap and bind the cmd_ptr pointer to mySample, as shown in:

 


(B) we use another mySample2 pointer to point to this object, as shown in:

 


After (C), reset operates on the first pointer object (p = NULL), but the CSample object is not released because its mySample2 is being referenced.

 


The memory of the CSample object is released only when the last reference is released and the current scope exists.

 


The following are some shared_ptr application cases:

Use in containers
Using the pointer-to-implementation idiom (PIMPL)
Resource-Acquisition-Is-Initialization (RAII) idiom
Separating Interface from Implementation
1> Use in containers;

2> PIMPL (pointer to implementation) Convention, that is, "short implementation pointer ";

3> RAII () practices; (For details, refer to "Study Notes (4)")

4> use interfaces and implementation separation of Classes

TIPS: PIMPL idiom and RAII idiom

1. RAII

RAII is a technology invented by Professor Bjarne Stroustrup to address resource allocation. Resource Acquisition is initialized. RAII is the direct use of the C ++ constructor, that is, the use of constructor to allocate resources and the use of destructor to recycle resources.

2. PIMPL

PIMPL is a widely used technology with many aliases, such as Opaque pointer and handle classes. PIMPL is an extension of RAII. It is controlled by RAII to remove the specific data layout and implementation from the caller's sight, thus simplifying the API interface, it also makes ABI compatibility possible. Qt and KDE use Pimpl to maintain ABI consistency. In addition, it also provides a way for inert initialization and a basis for implicit sharing.

Detailed introduction reference: http://c2.com/cgi/wiki? PimplIdiom or wiki;

PIMPL or RAII is a well-known concept in C ++ programs. Smart pointers are just one of the two conventional methods.

(If you never heard of PIMPL (a.k. a. handle/body) or RAII, grab a good C ++ book-they are important concepts every C ++ programmer shold know. smart pointers are just one way to implement them conveniently in certain cases)
 


(3) features of pai_ptr

Here, we will reference the share_ptr features described in Smart Pointers to boost your code,

Shared_ptr <T> works with an incomplete type:
When declaring or using a shared_ptr <T>, T may be an "incomplete type ". e. g ., you do only a forward declaration usingclass T ;. but do not yet define howT really looks like. only where you dereference the pointer, the compiler needs to know "everything ".

Shared_ptr <T> works with any type:
There are always Ally no requirements towards T (such as deriving from a base class ).

Shared_ptr <T> supports a custom deleter
So you can store objects that need a different cleanup than delete p. For more information, see the boost documentation.

Implicit conversion:
If a type U * can be implicitly converted to T * (e.g., becauseT is base class ofU), a shared_ptr <U> can also be converted toshared_ptr <T> implicitly.

Shared_ptr is thread safe
(This is a design choice rather than advantage, however, it is a necessity in multithreaded programs, and the overhead is low .)

Works on your platforms, proven and peer-reviewed, the usual things.
In general, shared_ptr can share and transfer ownership, and can be used by containers in the standard library. It is thread-safe and cannot point to a dynamically growing memory (replaced by javas_array.

(4) Example: Use javas_ptr in a container

Many container classes, including standard STL containers, require an inserting an existing element into a list, vector, or container ). However, when such replication operations are complex or difficult to implement or be available, pointer containers are a simple and effective solution. For example:

1: std: vector <CMyLargeClass *> vec; 2: vec. push_back (new CMyLargeClass ("bigString "));
The above program handed over the memory management task to its caller. here we can use mongo_ptr to rewrite it,

1: typedef boost: shared_ptr <CMyLargeClass> CMyLargeClassPtr; 2: std: vector <CMyLargeClassPtr> vec; 3: vec. push_back (CMyLargeClassPtr (new CMyLargeClass ("bigString ")));
In this way, the memory management of the task is very simple after the rewrite. when the container is destroyed, the elements in the container are also automatically destroyed.

However, if there are other smart pointers referencing it, the referenced element still exists and will not be released. The following program,


1: void Sample3_Container () 2: {3: typedef boost: shared_ptr <CSample> CSamplePtr; 4: 5: // (A) create a container of CSample pointers: 6: std: vector <CSamplePtr> vec; 7: 8: // (B) add three elements 9: vec. push_back (CSamplePtr (new CSample); 10: vec. push_back (CSamplePtr (new CSample); 11: vec. push_back (CSamplePtr (new CSample); 12: 13: // (C) "keep" a pointer to the second: 14: CSamplePtr anElement = vec [1]; 15: 16: // (D) destroy the vector: 17: vec. clear (); 18: 19: // (E) the second element still exists 20: anElement-> Use (); 21: printf ("done. cleanup is automatic \ n "); 22: 23: // (F) anElement goes out of scope, deleting the last CSample instance 24 :}

(5) Notes for using cmd_ptr

1. shared_ptr references the same data multiple times, as shown below:

1: {2: int * pInt = new int [100]; 3: boost: shared_ptr <int> sp1 (pInt); 4: // after some other code... 5: boost: shared_ptr <int> sp2 (pInt); 6 :}
This situation is very likely to happen in reality, and the result is also very fatal. It will cause two releases of the same memory and destroy the heap.

2. problems caused by using shared_ptr to wrap this pointer are as follows:

1: class tester 2: {3: public: 4: tester () 5 :~ Tester () 6: {7: std: cout <"destructor called! \ N "; 8:} 9: public: 10: boost: shared_ptr <tester> sget () 11: {12: return boost: shared_ptr <tester> (this ); 13:} 14 :}; 15: int main () 16: {17: tester t; 18: boost: shared_ptr <tester> sp = t. sget ();//... 19: return 0; 20 :}

It will also cause two t object releases to destroy the stack, one is the analysis structure at the time of the stack, and the other is the shared_ptr analysis structure. If you need this, you can use the following code.

1: class tester: public boost: enable_shared_from_this <tester> 2: {3: public: 4: tester () 5 :~ Tester () 6: {7: std: cout <"destructor called! \ N "; 8:} 9: public: 10: boost: shared_ptr <tester> sget () 11: {12: return shared_from_this (); 13:} 14 :}; 15: int main () 16: {17: boost: shared_ptr <tester> sp (new tester); 18: // use the sp pointer correctly. 19: sp-> sget (); 20: return 0; 21 :}
3. Memory leakage caused by shared_ptr loop reference. The Code is as follows:

1: class parent; 2: class child; 3: typedef boost: shared_ptr <parent> parent_ptr; 4: typedef boost: shared_ptr <child> child_ptr; 5: class parent 6: {7: public: 8 :~ Parent () {9: std: cout <"the parent class destructor is called. \ n "; 10 :}11: public: 12: child_ptr children; 13 :}; 14: class child 15: {16: public: 17 :~ Child () {18: std: cout <"subclass destructor called. \ n "; 19 :}20: public: 21: parent_ptr parent; 22 :}; 23: int main () 24: {25: parent_ptr father (new parent ()); 26: child_ptr son (new child); 27: // The Parent and child references each other. 28: father-> children = son; 29: son-> parent = father; 30: return 0; 31 :}
In the above Code, before the program exits, the reference count of father is 2 and the count of son is 2. When the program exits, shared_ptr simply reduces the count by 1, if it is 0, it is released. Obviously, in this case, the reference count is not 0, so the memory pointed to by father and son is not released, leading to memory leakage.

4. Notes for using shared_ptr in multi-threaded programs. The Code is as follows:

1: class tester 2: {3: public: 4: tester () {} 5 :~ Tester () {} 6: // more function definitions... 7 :}; 8: void fun (boost: shared_ptr <tester> sp) 9: {10 ://!!! A large number of sp pointers are used here. 11: boost: shared_ptr <tester> tmp = sp; 12:} 13: int main () 14: {15: boost: shared_ptr <tester> sp1 (new tester ); 16: // enable two threads and pass in the smart pointer for use. 17: boost: thread t1 (boost: bind (& fun, sp1); 18: boost: thread t2 (boost: bind (& fun, sp1 )); 19: t1.join (); 20: t2.join (); 21: return 0; 22 :}
The problem caused by this code is obvious, because multithreading is the same as accessing smart pointers and assigning them to other similar smart pointers, it is very likely that two threads simultaneously operate on the reference count (but not necessarily absolutely), leading to counting failure or invalidity, leading to program crash. If you do not know the root cause, if you cannot find this bug, you can only pray to God that the program can run normally.

The above Code may not be written normally, but the following code is the same as the above Code, as shown below:

1: class tester 2: {3: public: 4: tester () {} 5 :~ Tester () {} 6: public: 7: boost: shared_ptr <int> m_spData; // other types may exist. 8 :}; 9: tester gObject; 10: void fun (void) 11: {12 ://!!! A large number of sp pointers are used here. 13: boost: shared_ptr <int> tmp = gObject. m_spData; 14:} 15: int main () 16: {17: // multithreading. 18: boost: thread t1 (& fun); 19: boost: thread t2 (& fun); 20: t1.join (); 21: t2.join (); 22: return 0; 23 :}
The situation is the same. The solution to this problem is also very simple. You can easily solve this problem by using boost. weak_ptr. In the first case, modify the Code as follows:

1: class tester 2: {3: public: 4: tester () {} 5 :~ Tester () {} 6: // more function definitions... 7 :}; 8: void fun (boost: weak_ptr <tester> wp) 9: {10: boost: shared_ptr <tester> sp = wp. lock; 11: if (sp) 12: {13: // you can safely use the sp pointer here. 14:} 15: else 16: {17: std: cout <"the pointer has been released!" <Std: endl; 18:} 19:} 20: int main () 21: {22: boost: shared_ptr <tester> sp1 (new tester); 23: boost. weak_ptr <tester> wp (sp1); 24: // enable two threads and pass in the smart pointer for use. 25: boost: thread t1 (boost: bind (& fun, wp); 26: boost: thread t2 (boost: bind (& fun, wp )); 27: t1.join (); 28: t2.join (); 29: return 0; 30 :}
Boost. the weak_ptr pointer function is not weak at all. weak_ptr is a constrable and configurable pointer to manage shared_ptr without increasing the reference count. It can be easily transferred back to the shared_ptr pointer, use the weak_ptr.lock function to obtain a shared_ptr pointer. If the pointer has been released elsewhere, it returns an empty shared_ptr. You can also use weak_ptr.expired () to determine whether a pointer is released.

Boost. weak_ptr not only solves the security problem caused by multi-threaded access, but also solves the third issue of circular reference. The Children class code is modified as follows to break the circular reference:

1: class child 2: {3: public: 4 :~ Child () {5: std: cout <"subclass destructor called. \ n "; 6:} 7: public: 8: boost: weak_ptr <parent> parent; 9 :};
Because boost: weak_ptr does not increase the reference count, you can analyze the function correctly when exiting the function field.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.