Powered by
/src$ make
  • Home
  • About
  • Directory
  • Contact
  • Home
  • About
  • Directory
  • Contact

C++ RAII, Shared Pointers, Unique Pointers Example

11/9/2018

 

Introduction - RAII

RAII (Resource Acquisition is Initialization) is a programming concept in C++. We're going to describe it very simply:

Problem: Memory leaks occur when heap allocated (new keyword) variables/objects don't get deleted because of unexpected problems. For example...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Copyright srcmake.com 2018.
#include <iostream>
#include <stdexcept>
using namespace std;

void MemoryLeak()
    {
    int* srcmake = new int(42);
    cout << "Allocated a new variable on the heap." << endl;
    
    // Then, what if an exception occurs?
    throw std::invalid_argument("This function ends on this line.");
    
    cout << "We never get to this line of code!" << endl;
    
    // The 'srcmake' int is still on the heap, and won't be removed from RAM. This is a memory leak.
    delete srcmake;
    }

int main()
    {
    // Call the MemoryLeak function. It can throw exceptions, so put it in a try catch.
    try
        {
        MemoryLeak();
        }
    catch(const std::invalid_argument& e)
        {
        
        }
    
    cout << "Program finished." << endl;
    return 0;
    }
The above code has a memory leak, because a resource is initialized, but is never deleted. This is where RAII comes in...
Solution: Encapsulate variables in classes, and design the destructors to deallocate the resource from memory when the object itself is deleted. 

The reason this works is because the object will have it's destructor called when it's scope ends (for example, when the throw happens).

The best way to make this happen is with smart pointers. There are a few types, but we'll look at two in this blog post.

Unique Pointers

Unique pointers are a good RAII technique for objects you only want to have one pointer too. (Meaning, only one pointer is allowed access to the variable/object.)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Copyright srcmake.com 2018.
// C++ Unique Pointer.
#include <iostream>
#include <memory>
using namespace std;

class srcPizza
    {
    public:
        // Constructor.
        srcPizza()
            { cout << "Full pizza. :) (Constructor)\n"; }
            
        // Destructor
        ~srcPizza()
            { cout << "Pizza is gone. :( (Destructor)\n"; }
    };

void DoStuff()
    {
    //////////////////////////////////////////////////////
    // Declare a unique pointer like this:
    unique_ptr<int> srcUP(new int(42));
    // The constructor takes a pointer to the object is.
    
    // You can dereference the unique pointer like a normal pointer.
    cout << *srcUP << endl;
    //////////////////////////////////////////////////////
    
    
    //////////////////////////////////////////////////////
    // DO NOT pass a raw pointer as the constructor. 
    int* rawPtr = new int(52);
    unique_ptr<int> srcUP2(rawPtr);
    // ^ BAD. It works, but it's bad practice. 
    // Always use the new keyword inside of the unique pointer constructor.
    
    cout << *srcUP2 << endl;
    //////////////////////////////////////////////////////
    
    
    //////////////////////////////////////////////////////
    // In C++14, you can use make_unique instead of the new keyword.
    unique_ptr<int> srcUP3 = make_unique<int>(62);
    // It's safer. (Sort of.)
    
    cout << *srcUP3 << endl;
    //////////////////////////////////////////////////////
    
    
    //////////////////////////////////////////////////////
    // The destructor of an object is called when the unique pointer is deleted.
    unique_ptr<srcPizza> srcUP4(new srcPizza());    
    } // srcUP4 gets deleted here, so the destructor of the srcPizza is called.
    //////////////////////////////////////////////////////
    
    
int main()
    {
    cout << "Program started." << endl;
    DoStuff();
    cout << "Program finished." << endl;
    return 0;
    } 
The output of the above code (compiled with c++14) is: 
Program started.                                                                 
42                                                                               
52                                                                             
 62                                                                               
Full pizza. :) (Constructor)                                                     
Pizza is gone. :( (Destructor)                                                   
​Program finished.   

Shared Pointers

Shared Pointer is the same as unique pointers, except there can be multiple pointers to one variable/object. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Copyright srcmake.com 2018.
// C++ Shared Pointer.
#include <iostream>
#include <memory>
using namespace std;

class srcPizza
    {
    public:
        // Constructor.
        srcPizza()
            { cout << "Full pizza. :) (Constructor)\n"; }
            
        // Destructor
        ~srcPizza()
            { cout << "Pizza is gone. :( (Destructor)\n"; }
    };

void DoStuff()
    {
    //////////////////////////////////////////////////
    // Declare a shared pointer like this:
    shared_ptr<int> srcSP(new int(42));
    // The constructor takes a pointer to the object is.
    
    // You can make another pointer point to the same thing.
    shared_ptr<int> srcSP2 = srcSP;
    
    // You can dereference the shared pointer like a normal pointer.
    cout << *srcSP << endl;
    cout << *srcSP2 << endl;
    //////////////////////////////////////////////////
    
    
    //////////////////////////////////////////////////
    // Let's make a sharedPtr to an object.
    shared_ptr<srcPizza> srcSP3(new srcPizza());
    cout << "Constructor should have been called already.\n";
    shared_ptr<srcPizza> srcSP4 = srcSP3;
    
    // Reset will make the pointer not point to the object anymore.
    srcSP3.reset();
    cout << "SP3 no longer points to the pizza.\n";
    // But since SP4 still points to it, the object is alive. If we reset SP4, then the object is deleted, since no pointers point to it anymore.
    cout << "Resetting SP4.\n";
    srcSP4.reset();
    cout << "The destructor will have been called.\n";
    //////////////////////////////////////////////////
    
    
    //////////////////////////////////////////////////
    // The destructor of an object is called when the shared pointer goes out of scope.
    shared_ptr<srcPizza> srcSP5(new srcPizza());
    cout << "Going out of scope.\n";    
    } // srcSP5 gets deleted here, so the destructor of the srcPizza is called.
    //////////////////////////////////////////////////
    
    
int main()
    {
    cout << "Program started." << endl;
    DoStuff();
    cout << "Program finished." << endl;
    return 0;
    } 
The code will output the following:
​Program started.                                              
42
42
Full pizza. :) (Constructor)
Constructor should have been called already.
SP3 no longer points to the pizza.
Resetting SP4.
Pizza is gone. :( (Destructor)
The destructor will have been called.
Full pizza. :) (Constructor)
Going out of scope.
Pizza is gone. :( (Destructor)
Program finished. 

Conclusion

RAII is an important C++ technique, where the code is designed so that even unexpected code breaking will assure that objects are cleaned up and memory leaks don't occur. We've seen how Smart Pointers, such as unique pointer and shared pointer, allow us to automatically handle RAII, but RAII is a design principle that can be applied to even our custom classes where any type of resource that we're responsible for needs to be designed carefully. 

To explain it simply: Put a resource in the constructor of a class. Clean up that resource in the destructor of that class. And use a smart pointer to handle using that class. EZ.
​Like this content and want more? Feel free to look around and find another blog post that interests you. You can also contact me through one of the various social media channels. 

Twitter: @srcmake
Discord: srcmake#3644
Youtube: srcmake
Twitch: www.twitch.tv/srcmake
​Github: srcmake
References
1. www.fluentcpp.com/2017/08/22/smart-developers-use-smart-pointers-smart-pointers-basics/
​
2. www.fluentcpp.com/2018/02/13/to-raii-or-not-to-raii/
3. stackoverflow.com/questions/8480640/how-to-throw-a-c-exception
​
4. www.youtube.com/watch?v=ENj37HvptgU
​
5. stackoverflow.com/questions/22571202/differences-between-stdmake-unique-and-stdunique-ptr-with-new

Comments are closed.

    Author

    Hi, I'm srcmake. I play video games and develop software. 

    Pro-tip: Click the "DIRECTORY" button in the menu to find a list of blog posts.
    Metamask tip button
    License: All code and instructions are provided under the MIT License.

    Discord

    Chat with me.


    Youtube

    Watch my videos.


    Twitter

    Get the latest news.


    Twitch

    See the me code live.


    Github

    My latest projects.

Powered by Create your own unique website with customizable templates.