C++ smart pointer introduction

STL provides us with four kinds of smart Pointers: auto_PTR, Unique_PTR, shareD_PTR and Weak_PTR. Auto_ptr is a solution provided by C++98 and has been deprecated by C+11.

🍉 Reason for ditching auto_ptr: To avoid potential memory crashes. Auto_ptr may access null Pointers causing memory crashes.

All smart pointer classes have a explicit constructor that takes a pointer as an argument. For example, the auto_ptr class template prototype is:

templet<class T>
class auto_ptr {
  explicit auto_ptr(X* p = 0) ; . };Copy the code

This means that implicit type conversions between smart Pointers are not allowed. In addition:

// Smart Pointers should not be used to point to non-heap memory variables, especially temporary variables. Using delete will result in undefined behavior
string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation);   // No❗ (although this will not report an error)
Copy the code
  • When the program tries to put aunique_ptrWhen you assign to another one, if the originalunique_ptrIs a temporary rvalue, which the compiler allows; If the originalunique_ptrWill exist for a while, and the compiler will disallow this, for example:
unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1;                                      // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You"));   // #2 allowed
Copy the code
  • C++ has a standard library functionstd::move(), so you can put aunique_ptrTo another. This function returns oneunique_ptrObject. usemoveAfter the original pointer is changed to a null pointer, it can be reassigned:
unique_ptr<string> ps1, ps2;
ps1 = unique_ptr<string>(new string("hello")); 
ps2 = move(ps1);
ps1 = unique_ptr<string>(new string(" world"));
cout << *ps2 << *ps1 << endl;  // hello world
Copy the code

Smart pointer selection

🍊 To select shared_ptr:

  1. Multiple users share the same object without a clear owner

    • This is like the light in the classroom, everyone is using the light, but no one is responsible for the light switch. In this scenario, we can use Shared_PTR to manage the lamp throughshared_ptrAnyone can use the light but the last person to use it will be responsible for turning it off.
  2. Copying an object is time consuming

    • If copying an object is time-consuming and we need to pass the object between functions, we often choose to pass Pointers to the object instead of passing the object itself to avoid copying the object. Since you have chosen to use Pointers, useshared_ptrIs a better choice because it serves the purpose of passing objects to functions without having to worry about releasing them.
  3. To store Pointers to the library container

    • The STL container contains Pointers. Many STL algorithms support copy and assignment operations, which are availableshared_ptr. No matter whether ordinary Pointers or smart Pointers are stored in the container, there is no big difference between them in use. The advantage of using smart Pointers is mainly reflected in the operation of clearing the container after use. If a container holds ordinary Pointers, when we empty a container, we must release the resources that the Pointers point to before we can empty the Pointers themselves.
  4. When managing resources that require a special cleanup mode, you can customize the shared_ptr remover to do so

🍒 select unique_ptr:

If your program does not need multiple Pointers to the same object, use unique_ptr. If a function allocates memory using new and returns a pointer to that memory, it is a good choice to declare its return type as unique_ptr. This transfers ownership to the unique_PTR that accepts the return value, and the smart pointer is responsible for calling delete. Unique_ptr can be stored in an STL container, as long as there is no call to copy or assign a Unique_Ptr to another algorithm (such as sort()).

unique_ptr<int> make_int(int n) {
    return unique_ptr<int> (new int(n));
}

void show(unique_ptr<int> &p1) {
    cout << *a << ' ';
}

int main(a)
{... vector<unique_ptr<int> > vp(size);
    for(int i = 0; i < vp.size(a); 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(). }Copy the code

If objects are passed by value rather than by reference to show(), for_each() would be illegal, as this would result in PI being initialized with a non-temporary unique_ptr from vp, which is not allowed. The compiler will detect an attempt to use unique_ptr incorrectly.

When unique_ptr is a temporary rvalue, it can be assigned to shared_Ptr in the same way that a unique_ptr is assigned to a condition that needs to be satisfied. As before, in the following code, make_int() returns type unique_ptr:

unique_ptr<int> pup(make_int(rand() % 1000));   // ok
shared_ptr<int> spp(pup);                       // not allowed, not temporary rvalue
shared_ptr<int> spr(make_int(rand() % 1000));   // ok
Copy the code

weak_ptr

Although the C++11 standard positioning weak_ptr as a smart pointer, but this type of pointer is usually not used alone (no practical use), can only be used with shared_ptr type pointer. Even, we can regard weak_PTR type pointer as an auxiliary tool of shareD_PTR pointer. With weak_PTR type pointer, we can obtain some state information of shareD_PTR pointer. For example, how many shared_ptr Pointers to the same heap, whether shared_ptr Pointers to heap memory has been freed, and so on.

Note that when a Weak_ptr pointer points to the same value as a shared_ptr pointer, a Weak_ptr pointer does not increase the reference count of the pointed heap memory by 1. Similarly, when the Weak_ptr pointer is released, the reference count of the previously indicated heap memory is not decreased by 1. That is, a Weak_Ptr type pointer does not affect the reference count of the indicated heap memory space.

In addition, there are no overloaded * and -> operators in the Weak_ptr template class, which means that the Weak_ptr type pointer can only access the pointed heap memory, not modify it.

For weak references, weak references do not necessarily exist while the referenced object is alive. A weak reference does not modify the object’s reference count, which means that it does not manage the object’s memory.

Weak_ptr is functionally similar to a normal pointer, but one big difference is that a weak reference can detect whether the managed object has been freed, thus avoiding access to illegal memory.

Note: although by a weak reference pointer can effectively remove circular reference, but this way must be in the programmer can foresee there will be a circular reference to use, also can be said that this is just a compile time solution, if the program appeared in the process of running a circular reference, still can cause a memory leak.