Smart pointer

  • Hey guys! It has been several days since I last wrote a blog like this. I have been busy with mid-term exams for days. But now I’m back!
  • This is the first time I have written a blog entirely in English. Some statements or terminology may sound strange or be incorrect, but I am excited to start and improve my skills in English blogging. There are several reasons for this, but the main ones are as follows:
    1. Practicing English.
    2. Becoming familiar with terminology in the computer science field.
    3. Having a better ability to learn English materials related to computer science.
  • I would like to give a special thanks to @The Cherno for teaching me C++ through his C++ series on YouTube. Please subscribe to his channel if possible!

Brief Introduction

  • Smart pointers can help you manage your memory automatically, preventing the issue of forgetting to call “delete” to free memory allocated by “new”.

Premise

  • We have some libs included and the class “Entity”.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include<iostream>
    #include<string>
    #include<memory> //include smart pointer

    class Entity
    {
    public:
    Entity()
    {
    std::cout << "Created Entity!" << std::endl;
    }

    ~Entity()
    {
    std::cout << "Destroyed Entity!" << std::endl;
    }

    void Print() {}
    };

unique_ptr

  • We have an simple demo of unique_ptr here. Notice that the curly braces just for marking the scope.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ... // We omit the previous code
    int main()
    {
    {// explicit "must use the construction function"

    std::unique_ptr<Entity> entity = std::make_unique<Entity>();

    entity->Print();
    }// the curly braces mark the scope

    //end of main
    std::cin.get();
    }
  • The unique_ptr cannot be copied! And that is the meaning of its name. This means that you cannot use unique_ptr like this:
    1
    2
    3
    4
    5
    6
    7
    8
    class Example
    {
    ...//omit
    };
    ...//omit
    std::unique_ptr<Example> uniq_ptr = std::make_unique<Example>();
    void* ptr
    ptr = uniq_ptr // invalid, you can't copy uniq_ptr
  • If you try to do so, you will find that the compiler throws an error. You can find the reason in the place where unique_ptr is defined or just check it in Cherno’s C++ series.
  • But hey, if you’re feeling rebellious and want to break the rules, there are some tricky ways to move a unique_ptr around using move constructors and assignment operators. Just don’t blame me when your program crashes and burns faster than a flaming marshmallow!

shared_ptr

  • The shared_ptr is implemented using a counter.
  • When another shared_ptr shares an existing pointer, the counter is incremented.
  • When a shared_ptr is deleted, the counter is decremented.
  • The memory will only be freed when the counter reaches zero.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
std::shared_ptr<Entity> entity;
// we created entity here, but didn't initialize
{
std::shared_ptr<Entity> shared_entity = std::make_shared<Entity>();

entity = shared_entity; // we didn't fulfill operator useability here

entity->Print();
}
// ptr "shared_entity" is unavaible here, but it's memory isn't free
// because ptr "entity" hold it's memory
}

weak_ptr

  • weak_ptr can be considered a special kind of shared_ptr. It can refer to a shared_ptr, but doesn’t affect the reference count.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main()
    {
    std::weak_ptr<Entity> entity;
    // we created entity here, but didn't initialize
    {
    std::shared_ptr<Entity> shared_entity = std::make_shared<Entity>();

    entity = shared_entity;

    shared_entity->Print();
    }
    // shared_entity died here because the counter turn to 0.
    //but we can ask weak_ptr whether it's memory was free
    }
  • After all shared_ptr instances have been destroyed, a weak_ptr cannot hold the memory. However, we can use a weak_ptr to check whether its associated memory has been freed. It is like asking it, “Hey, is your memory freed?”.

Summary

  • In summary, smart pointers in C++ are used to manage memory allocation and deallocation automatically to avoid memory leaks and dangling pointers. We mainly covers the most commonly used smart pointers in C++. They are implemented as templates and have three types: unique_ptr, shared_ptr, and weak_ptr.
  • unique_ptr is used to manage exclusive ownership of an object, meaning that it cannot be copied and can only be moved. It is useful when a single object is managed by one pointer, and the object should be automatically destroyed when the pointer is out of scope.
  • shared_ptr is used to manage shared ownership of an object, meaning that multiple pointers can refer to the same object. It uses reference counting to keep track of how many pointers are referring to the object, and the object is automatically destroyed when the reference count goes to zero.
  • weak_ptr is used in conjunction with shared_ptr to avoid cyclic dependencies, where objects refer to each other in a way that cannot be broken. It provides a non-owning reference to an object managed by shared_ptr, and can be used to check whether the object has been destroyed. (Actually I am not familiar with weak_ptr, so I just search some material through Internet. May be we will talk about it later.)
  • Overall, smart pointers are a powerful feature in C++ that allow for more reliable memory management, and can help prevent common programming errors related to memory allocation and deallocation.