Simple Analysis of C ++ smart pointers and smart pointer Analysis

Source: Internet
Author: User

Simple Analysis of C ++ smart pointers and smart pointer Analysis
Guide

I recently made up the sixth edition of C ++ Primer Plus. This is indeed a good book. The chapter on smart pointers is very clear, and I have encountered many puzzles. During the C ++ interview, Many interviewers like to ask questions related to smart pointers. For example, what smart pointers do you know? What is the design principle of shared_ptr? How do you design a smart pointer on your own? And so on ....... In addition, you can see the shadows of smart pointers everywhere when looking at open-source C ++ projects. This shows that smart pointers are not only the subject matter that the interviewer loves to ask, but also very useful.

The following are my notes on smart pointers, hoping to solve some of your problems with smart pointers.

Directory
  1. Design ideas behind Smart pointers
  2. C ++ smart pointer
  3. Why abandon auto_ptr?
  4. Why is unique_ptr better than auto_ptr?
  5. How do I select smart pointers?
Text 1. design ideas behind Smart pointers

Let's look at a simple example:

void remodel(std::string & str){    std::string * ps = new std::string(str);    ...    if (weird_thing())        throw exception();    str = *ps;     delete ps;    return;}

When an exception occurs (weird_thing () returns true), the delete operation is not executed, resulting in Memory leakage.
How can this problem be avoided? Some people may say that this is not simple.throw exception();Adddelete ps;No. Yes, you should have done this. The problem is that many people will forget to add the delete statement in the appropriate place (even the last delete statement in the above Code will be forgotten by many people ), if you want to review a huge project to see if there is such a potential memory leakage problem, it is a disaster!
In this case, we will consider: When a function such as remodel is terminated (whether it is terminated normally or due to an exception ), the local variables will be automatically deleted from the stack memory. Therefore, the memory occupied by the pointer ps will be released,How nice it would be if the memory to which the ps points is also automatically released.
We know that the Destructor has this function. If ps has a destructor, The Destructor will automatically release the memory it points to When ps expires. But the problem with ps is that it is just a regular pointer, not a Class Object Pointer with the number of destructor. If it points to an object, you can let its destructor Delete the memory pointed to when the object expires.

This is exactly the design idea behind the smart pointers auto_ptr, unique_ptr, and shared_ptr. My summary is:Encapsulate the basic type pointer as a Class Object Pointer (this class must be a template to meet the needs of different basic types ), write the delete statement in the destructor to delete the memory space pointed to by the pointer.

Therefore, to convert the remodel () function, follow these three steps:

  • Contains the header file memory (the header file containing the smart pointer );
  • Replace the pointer pointing to string with the smart pointer object pointing to string;
  • Delete A delete statement.

The following is the result of modifying the function using auto_ptr:

# include <memory>void remodel (std::string & str){    std::auto_ptr<std::string> ps (new std::string(str));    ...    if (weird_thing ())        throw exception();     str = *ps;     // delete ps; NO LONGER NEEDED    return;}

2. Introduction to C ++ smart pointers

STL provides four smart pointers: auto_ptr, unique_ptr, shared_ptr, and weak_ptr ).
The template auto_ptr is a solution provided by C ++ 98. C + 11 has abandoned it and provides two other solutions. However, although auto_ptr has been abandoned, it has been used for many years: At the same time, if your compiler does not support the other two solutions, auto_ptr will be the only choice.

Usage notes

  • All smart pointer classes have an explicit it constructor, which uses pointers as parameters. For example, the auto_ptr class template prototype is:
    templet<class T>class auto_ptr {    explicit auto_ptr(X* p = 0) ;     ...};

    Therefore, the pointer cannot be automatically converted to a smart pointer object and must be explicitly called:

    shared_ptr<double> pd; double *p_reg = new double;pd = p_reg;                               // not allowed (implicit conversion)pd = shared_ptr<double>(p_reg);           // allowed (explicit conversion)shared_ptr<double> pshared = p_reg;       // not allowed (implicit conversion)shared_ptr<double> pshared(p_reg);        // allowed (explicit conversion)
  • One of the three smart pointers should be avoided:
    string vacation("I wandered lonely as a cloud.");shared_ptr<string> pvac(&vacation);   // No
    When pvac expires, the program will use the delete operator for non-heap memory, which is incorrect.

Example

#include <iostream>#include <string>#include <memory>class report{private:    std::string str;public: report(const std::string s) : str(s) {  std::cout << "Object created.\n"; } ~report() {  std::cout << "Object deleted.\n"; } void comment() const {  std::cout << str << "\n"; }};int main() { {  std::auto_ptr<report> ps(new report("using auto ptr"));  ps->comment(); } {  std::shared_ptr<report> ps(new report("using shared ptr"));  ps->comment(); } {  std::unique_ptr<report> ps(new report("using unique ptr"));  ps->comment(); } return 0;}

3. Why discard auto_ptr?

Let's take a look at the following assignment statement:

auto_ptr< string> ps (new string ("I reigned lonely as a cloud.”);auto_ptr<string> vocation; vocaticn = ps;

What will the above assignment statement do? If ps and vocation are regular pointers, the two pointers point to the same string object. This is unacceptable because the program will try to delete the same object twice-one is when the ps expires and the other is when the vocation expires. To avoid this problem, there are multiple methods:

  • Define the limit value operator to perform deep replication. In this way, the two pointers will point to different objects. One of the objects is a copy of another object. The disadvantage is that it is a waste of space. Therefore, smart pointers do not adopt this solution.
  • Establish the ownership concept. For a specific object, only one smart pointer can be possessed, so that only the constructor that owns the object will delete the object. Then let the assignment operation transfer ownership. This is the policy used for auto_ptr and uniqiie_ptr, but the policy of unique_ptr is stricter.
  • Create smart and higher pointers to track the number of smart pointers that reference specific objects. This is called reference count. For example, when a value is assigned, the Count value is 1, and when the pointer expires, the Count value is reduced by 1 ,. Delete is called only when it is reduced to 0. This is the shared_ptr policy.

Of course, the same policy applies to the replication constructor.
Each method has its own purpose, but why should we discard auto_ptr?
The following is an example.

# Include <iostream> # include <string> # include <memory> using namespace std; int main () {auto_ptr <string> films [5] = {auto_ptr <string> (new string ("Fowl bils ")), auto_ptr <string> (new string ("Duck Walks"), auto_ptr <string> (new string ("Chicken Runs ")), auto_ptr <string> (new string ("Turkey Errors"), auto_ptr <string> (new string ("Goose Eggs")}; auto_ptr <string> pwin; pwin = films [2]; // films [2] loses ownership. transfer The ownership from films [2] to pwin. In this case, films [2] no longer references this string and turns it into a null pointer cout <"The nominees for best avian baseballl film are \ n "; for (int I = 0; I <5; ++ I) cout <* films [I] <endl; cout <"The winner is" <* pwin <endl; cin. get (); return 0 ;}

The program crashed after running. The cause is clearly stated in the above comment. films [2] is already a null pointer, and the output below will of course crash when accessing the NULL pointer. However, if we replace auto_ptr with shared_ptr or unique_ptr, the program will not crash for the following reasons:

  • When shared_ptr is used, the operation runs normally. Because shared_ptr uses reference counting, both pwin and films [2] point to the same memory, when releasing a space, there is no error in deleting an object multiple times because you must determine the size of the reference count value in advance.
  • A compilation error occurs when unique_ptr is used. Like auto_ptr, unique_ptr also adopts the ownership model. However, when unique_ptr is used, the program will not wait until the runtime crashes, but the compiler will encounter an error due to the following code lines:
    unique_ptr<string> pwin;pwin = films[2]; // films[2] loses ownership.
    Guides you to discover potential memory errors.

This is the reason why auto_ptr should be abandoned. One sentence is summarized as follows:Avoid potential memory crashes.

4. Why is unique_ptr better than auto_ptr?

We may think that the previous example shows why unique_ptr is better than auto_ptr, which is a security issue. The following is a clear description.
See the following statement:

See the following statement:

auto_ptr<string> p1(new string ("auto") ; //#1auto_ptr<string> p2;                       //#2p2 = p1;   

In statement #3, after p2 takes over the ownership of the string object, the ownership of p1 is denied. As mentioned above, this is a good thing. It can prevent the destructor of p1 and p2 from attempting to merge the same-object;
However, if the program then tries to use p1, this will be a bad thing, because p1 no longer points to valid data.

The following shows how unique_ptr is used:

unique_ptr<string> p3 (new string ("auto");   //#4unique_ptr<string> p4;                       //#5p4 = p3;    

The compiler considers statement #6 invalid, avoiding the problem that p3 no longer points to valid data. Therefore, unique_ptr is safer than auto_ptr.

However, unique_ptr is more intelligent.
Sometimes, a smart pointer is assigned to another, which does not leave a dangerous hanging pointer. Assume there are the following function definitions:

unique_ptr<string> demo(const char * s){    unique_ptr<string> temp (new string (s));     return temp;}

The following code is written:

unique_ptr<string> ps;ps = demo('Uniquely special");

Demo () returns a temporary unique_ptr, and ps takes over all the objects originally returned by the unique_ptr, and the temporary unique_ptr is destroyed when the returned result is returned, that is, there is no chance to use unique_ptr to access invalid data, in other words, this assignment will not cause any problems, that is, there is no reason to prohibit this assignment. In fact, the compiler does allow this assignment, which is more intelligent than unique_ptr.

In short, when the party program tries to assign a unique_ptr value to another, if the source unique_ptr is a temporary right value, the compiler will allow this; if the source unique_ptr will exist for a period of time, the compiler will disable this.For example:

unique_ptr<string> pu1(new string ("hello world"));unique_ptr<string> pu2;pu2 = pu1;                                      // #1 not allowedunique_ptr<string> pu3;pu3 = unique_ptr<string>(new string ("You"));   // #2 allowed

Where #1 leaves the hanging unique_ptr (pu1), which may cause harm. #2 does not leave the hanging unique_ptr because it calls the unique_ptr constructor. The temporary object created by the constructor will be destroyed after the ownership is granted to pu3.This behavior only shows that unique_ptr is better than auto_ptr that allows two values.

Of course, you may want to perform operations similar to #1. This assignment is not safe only when the discarded smart pointer is used in a non-intelligent way (for example, when referencing is removed. To safely reuse this pointer, you can give it new values. C ++ has a standard library function std: move (), allowing you to assign one unique_ptr to another. The following is an example of using the preceding demo () function. This function returns a unique_ptr <string> object:
After moving is used, the original pointer still transfers ownership to a null pointer, And you can assign a value to it.

unique_ptr<string> ps1, ps2;ps1 = demo("hello");ps2 = move(ps1);ps1 = demo("alexia");cout << *ps2 << *ps1 << endl;

5. How do I select smart pointers?

What kind of smart pointers should be used after these smart pointers are mastered?
(1) If the program wants to use multiple pointers pointing to the same object, select shared_ptr. Such situations include:
There is an array of pointers, and some secondary pointers are used to indicate specific elements, such as the largest element and the smallest element;
Both objects contain pointers pointing to the third object;
STL containers contain pointers.
Many STL algorithms support copying and assigning values. These operations can be used for shared_ptr, but cannot be used for unique_ptr (the compiler sends a warning) and auto_ptr (the behavior is uncertain ). If your compiler does not provide shared_ptr, you can use shared_ptr provided by the Boost library.

(2) If the program does not need multiple pointers to the same object, you can use unique_ptr. If the function uses new to allocate memory and returns the pointer to the memory, it is a good choice to declare the return type as unique_ptr. In this way, the ownership is transferred to the unique_ptr that accepts the returned value, and the smart pointer is responsible for calling delete. The unique_ptr can be stored in the STL container, as long as one unique_ptr is copied or assigned to another algorithm (such as sort () without calling ()). For example, the program CNPC can be similar to the following code segment.

unique_ptr<int> make_int(int n){    return unique_ptr<int>(new int(n));}void show(unique_ptr<int> &p1){    cout << *a << ' ';}int main(){    ...    vector<unique_ptr<int> > vp(size);    for(int i = 0; i < vp.size(); i++)        vp[i] = make_int(rand() % 1000);              // copy temporary unique_ptr    vp.push_back(make_int(rand() % 1000));     // ok because arg is temporary    for_each(vp.begin(), vp.end(), show);           // use for_each()    ...}

The push_back call is correct because it returns a temporary unique_ptr, which is assigned to a unique_ptr in the vp. In addition, if the object is passed by value rather than by reference to show (), for_each () will be invalid because it will lead to the use of a non-temporary unique_ptr from vp to initialize pi, this is not allowed. As mentioned above, the compiler will find an attempt to use unique_ptr incorrectly.
When unique_ptr is the right value, you can assign it to shared_ptr, which is the same as assigning a unique_ptr to a condition that needs to be met. In the following code, the return type of make_int () is unique_ptr <int>:

unique_ptr<int> pup(make_int(rand() % 1000));   // okshared_ptr<int> spp(pup);                       // not allowed, pup as lvalueshared_ptr<int> spr(make_int(rand() % 1000));   // ok
The template shared_ptr contains an explicit constructor that can be used to convert the right value unique_ptr to shared_ptr. Shared_ptr takes over all objects originally owned by unique_ptr.
Auto_ptr can also be used when unique_ptr meets the requirements, but unique_ptr is a better choice. If your compiler does not have unique_ptr, consider using scoped_ptr provided by the Boost library, which is similar to unique_ptr.

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.