Smart Pointers in C++



A smart pointer is a class template that wraps around a raw pointer. It is used in memory management to clear the dynamically allocated memory automatically when it is no longer needed. This prevents memory leaks and dangling pointers. They are implemented as templates in the Standard Template Library (STL) (<memory> header).

Memory Leak: In C++, we need to manually clear the dynamically allocated memory. Memory leak occurs when the dynamically allocated memory is not cleared.
Dangling Pointer: It is a pointer that points to the memory address that has been cleared earlier but it still pointing to that deallocated memory address.

Here is an example of memory leak and dangling pointer where the ptr pointer causes a memory leak and the ptr2 pointer becomes a dangling pointer.

#include <iostream>
using namespace std;

int main(){   
    int *ptr1 = new int(42);    // allocating memory
    cout << "ptr1 pointer Value: " << *ptr1 << endl;
    // Here memory leak occurs as we have
    // not freed the allocated memory

    int *ptr2 = new int(100);   // allocating memory
    delete ptr2;                // clearing the memory
    // accessing freed memory
    cout << "ptr2 pointer value: " << *ptr2 << endl; // dangling pointer
    return 0;
}

The output of the above code is as follows −

ptr1 pointer Value: 42
ptr2 pointer value: 1465330567

Types of Smart Pointers

Smart pointers are of 4 types that are mentioned below. You need to use <memory> header to use these smart pointers −

The auto_ptr Pointer

The auto_ptr is a type of smart pointer that automatically manages memory. It is now deprecated since C++11, and removed in C++17 as it uses copy assignments operator to transfer ownership automatically without warning and the original pointer becomes null unexpectedly. This leads to unexpected null pointer access and program can crash.

Here is a simple example of auto_ptr where we are printing the value of ptr1 and ptr2. It will show a warning in the output as it has been deprecated

#include <iostream>
#include <memory>
using namespace std;

int main() {
    auto_ptr<int> ptr1(new int(100));
    cout << "ptr1 value: " << *ptr1 << endl;

    auto_ptr<int> ptr2 = ptr1;
    cout << "ptr2 value: " << *ptr2 << endl;
    return 0;
}

The output of the above code is as follows. As we can see here it gives a warning that auto_pointer has been deprecated −

main.cpp: In function 'int main()':
main.cpp:7:5: warning: 'template<class> class std::auto_ptr' is deprecated: use 
    'std::unique_ptr' instead [-Wdeprecated-declarations]
    7 |     auto_ptr<int> ptr1(new int(100));

ptr1 value: 100
ptr2 value: 100

The unique_ptr Pointer

The unique_ptr is a smart pointer that owns only one object at a time. It does not copy the pointers from one pointer to other. It transfers its ownership using std::move() function and automatically deletes the memory when the pointer goes out of scope. It solves the problem of auto_ptr and prevent memory leaks. You can use unique_ptr for single ownership.

unique ptr

Below is an example of unique_ptr where the pointer ptr1 transfers its ownership to pointer ptr2 to print the pointer value −

#include <iostream>
#include <memory>
using namespace std;

int main() {
    unique_ptr<int> ptr1 = make_unique<int>(200);
    cout << "ptr1 value: " << *ptr1 << endl;

    unique_ptr<int> ptr2 = move(ptr1);
    cout << "ptr2 value: " << *ptr2 << endl;
    return 0;
}

The output of the above code is as follows:

ptr1 value: 200
ptr2 value: 200

The shared_ptr Pointer

A shared_ptr is a smart pointer that allows multiple pointers to share ownership of the same object using reference counting, unlike unique_ptr where only one pointer can have the ownership of an object.

It maintains a reference count to keep a track of number of pointers sharing one object. The reference count is decreased when a shared_ptr is destroyed and the object is automatically deleted when count reaches zero. You can use shared_ptr when you need multiple owners of the same object.

Shared PTR

Here is an example of shared_ptr where both the pointers share the same object

#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> ptr1 = make_shared<int>(300);
    cout << "ptr1 value: " << *ptr1 << endl;
    cout << "Reference count: " << ptr1.use_count() << endl;

    shared_ptr<int> ptr2 = ptr1; // Both can access the object
    cout << "\nAfter sharing:" << endl;
    cout << "ptr1 value: " << *ptr1 << endl;
    cout << "ptr2 value: " << *ptr2 << endl;
    cout << "Reference count: " << ptr1.use_count() << endl;
    return 0;
}

The output of the above code is as follows −

ptr1 value: 300
Reference count: 1

After sharing:
ptr1 value: 300
ptr2 value: 300
Reference count: 2

The weak_ptr Pointer

A weak_ptr is a non-owning pointer to an object. Unlike shared_ptr, it does not increase the reference count of an object, and it can detect if the object has been destroyed. You can access the object using weak_ptr, but first you must lock the weak_ptr. By locking you get a temporary shared_ptr. It is used to avoid the circular dependency between shared_ptr objects.

Weak PTR

Here is an example of accessing an object value 400 using weak_ptr observer

#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> owner = make_shared<int>(400);
    weak_ptr<int> observer = owner;
    cout << "shared_ptr reference count: " << owner.use_count() << endl;

    // Lock to use weak_ptr
    if (auto locked = observer.lock())
    {
        cout << "Object value: " << *locked << endl;
    }
    owner.reset();
    cout << endl;

    return 0;
}

The output of the above code is as follows −

shared_ptr reference count: 1
Object value: 400

Pointer vs Smart Pointer

The following table differentiates between a pointer and a smart pointer

Pointer Smart Pointer
A pointer stores memory address of another variable or object. A smart pointer is a class template that wraps around a raw pointer and used for automatic memory management.
Manually new and delete is called to allocate and deallocate memory. It uses RAII(Resource Acquisition is Initialization) principles to automatically manage memory for allocation and deallocation.
It creates chances of memory leak and dangling pointers. No risk of memory leaks or dangling pointers.
There is no feature to track the numbers of pointers pointing to an object. Here, shared_ptr maintains a reference counter to track the number of pointers pointing to the same object.
It is not destroyed when it goes out of its scope. It gets automatically destroyed when it goes out of scope.

Conclusion

Smart pointers in C++ help in automatic memory management that solves the problem of memory leak and dangling pointers. There are four types of smart pointers: auto_ptr, unique_ptr, shared_ptr, and weak_ptr, out of which the auto_ptr has been deprecated.

Advertisements