Key points for counting smart pointers (shared_ptr)

Source: Internet
Author: User
Introduction: unlike many modern languages such as Java that support GC, C/C ++ gives more control of memory to programmers, while ensuring efficiency, it also gives you many opportunities to make mistakes. Common memory leaks, repeated releases, and so on. Smart pointers greatly reduce the chances of making mistakes, simplify code, and improve maintainability. Common smart pointers include scope_ptr (C ++ 11 unique_ptr). With raiI, resources are released when the scope is invalid, and stack rollback can be used to release resources when exceptions are thrown; the reference count smart pointer (shared_ptr) is used to share a resource with multiple owners. Therefore, it is easy to delete the resource by mistake or even do not know whether to delete the resource. Shared_ptr ensures that only the last owner can release resources once. 1. In simple terms, shared_ptr uses an owner count to track the pointer it manages to ensure that the pointer is released when there is no owner. Therefore, this count needs to be shared. You can outline the sharedptr members as follows: pseudo code: Class shared_ptr {int * m_ref; // reference count T * m_ptr;}; If m_ptr is not empty, m_ref should be initialized to 1: m_ref = new int (1); the copy constructor and the value assignment function of shared_ptr should increase the Resource Count: ++ * m_ref; the number of destructor decreases: if (m_ref & -- * m_ref = 0) {Delete m_ref; Delete m_ptr;} 2. from the above, we can see that sharedptr not only manages resources, but also counts, and only supports the default Delete release of resources. Not very convenient. Therefore, wrap the reference count and automatically release itself when the count falls by 0. Pseudocode: Class counterbase {counterbase (): m_ref (1) {} virtual ~ Counterbase () {} void destroy () {Delete this;} void decref () {If (-- m_ref = 0) destroy ();} // other operations are omitted: int m_ref ;}; class shared_ptr {counterbase * m_ref; // reference the count. You do not need to worry about T * m_ptr ;}; 3. as mentioned above, resource release only supports Delete. Aside from the array new, consider the following situations: file * fp = fopen (XXX); shared_ptr <File> file (FP ); obviously, you do not want to execute Delete FP, but fclose (FP). Therefore, you need to pass the second parameter to sharedptr: deleter func object to move m_ptr to the counterbase class to facilitate resource management, due to the uncertain method of releasing resources Virtual function dispose (); Class counterbase {void decref () {If (-- m_ref = 0) {+ dispose (); destroy ();}} + virtual void dispose (); // release resources + T * m_ptr;}; derives a class. The base class has a deleter object m_deleter.dispose (), which is implemented by default by Delete m_ptr; the custom implementation is m_deleter (m_ptr). For the above example, we can input such deleter: struct closefile {void operator () (File * FP) {If (FP) fclose (FP) ;}}; for C ++ 11, you can use lamdba to avoid this redundant definition. File * fp = fopen (XXX); shared_ptr <File> file (FP, closefile (); in this way, FP can be normally disabled. 4. The multi-threaded C ++ 20 years ago only considered a single thread, but it has long been different. To ensure the thread security of counting, the atomic Series Function packaging is required. Simply replace the original ++ -- operation with the corresponding API. The boost document provides some examples. If you are familiar with the implementation principle, it is easy to understand why some operations are not thread-safe. Analyze some examples in the official documents. Examples: shared_ptr <int> P (New int (42); // Ex 1 // thread ashared_ptr <int> P2 (P ); // thread bshared_ptr <int> P3 (p); this is safe. Because it is safe for two threads to execute the atomic incrementing function for counting at the same time. // EX 2 // thread AP. Reset (New int (1912); // thread bp2.reset; this is safe. Although the two smart pointers direct to the same resource, the reset operation only directs the counting pointer and resource pointer inside the smart pointer to the new resource. For old resources, it is safe to execute the atomic decreasing function at the same time. // Ex 3 // thread ap = P3; // thread bp3.reset (); this is not safe. Consider the following execution stream: thread a thread B

Decrease the number of internal references, and just decrease to 0; // 1

Increase the internal reference count; // 2. Release the resource and delete the reference count (assuming that weak_ptr does not reference this resource) // 3... Wait! // Ex 4 // thread ap3 = P2; // thread bp2 goes out of scope; Same principle as above, unsafe behavior; // ex 5 // thread ap3.reset (New int (1); // thread bp3.reset (New int (2); the same principle is true for insecure behaviors;

5. Consider the following situations for cyclic reference: Class B; // The complete definition of Class B is not required. Because shared_ptr only has pointer members and does not perform operations such as Delete. Class A {shared_ptr <B> m_pb ;}; Class B {shared_ptr <A> m_pa ;}; shared_ptr <A> PA (new ); shared_ptr <B> Pb (New B); pa-> m_pb = Pb; Pb-> m_pa = PA; you will find that because Pa and Pb point to each other, the reference count is not 0, and the resource is not released. Therefore, we have introduced something called weak_ptr. In essence, it is not a smart pointer. If a resource needs to be accessed, it constructs a shared_ptr, it can be considered as a reference count (weak_count), not a Resource Count (resource_count ). Therefore, you only need to change shared_ptr <A> m_pa; To weak_ptr <A> m_pa; and the resource can be released normally. At this time, it is very likely that the reference counting object still exists, as long as weak_ptr points to it; therefore, to use weak_ptr, you must first call lock () to obtain shared_ptr. If it is not empty, you can access the resource. 6. typical error struct a {int A ;}; shared_ptr <A> PA (new A); shared_ptr <int> pint (& pa-> ); the above code is obviously incorrect; member A belongs to object A, and its lifecycle is controlled by the latter. Shared_ptr provides a function to become alias ctor. The correct usage is as follows: shared_ptr <int> pint (Pa, & pa-> A); pint still shares Resource Count with Pa. However, the pointer of a pint package is different. This is probably why shared_ptr has a T * and the counter object also has a T *, which seems to be repeated, but it is not actually. Reference the example in the book: int * P = new int (0); shared_ptr <int> SP1 (p); shared_ptr <int> SP2 (p); this is incorrect, P will be released twice. The correct practice is: shared_ptr <int> SP2 (SP1). In addition, pay attention to the usage of enable_shared_from_this. It is easy to write the weak_ptr implementation. Shared_ptr has a small amount of code and is very useful. It is strongly recommended that you manually implement shared_ptr by yourself if you are not familiar with it. You can refer to the boost example, but do not look at the boost code implementation. After implementation, you can compare the differences and reasons.

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.