Smart Pointers.
Example : Smart pointer ( unique pointer ) vis-à-vis a raw pointer.
#include<iostream>
#include<memory>
#include<string>
using namespace std;
class AudioBook {
private:
string title;
string author;
public:
AudioBook() {}
AudioBook (string arg_title, string arg_author): title(arg_title), author(arg_author)
{}
inline void PlayBook() {
cout << "Playing : " << title << endl;
}
};
void With_UniquePointer(void) {
unique_ptr<AudioBook> uptr_a(new AudioBook("Lord Of The Rings", "J. R. R. Tolkien"));
uptr_a->PlayBook();
// Note : make_unique function efficiently creates and returns a unique pointer and is recommended over
// directly calling unique_ptr constructor.
auto uptr_b = make_unique<AudioBook>("Game Of Thrones", "George RR Martin");
uptr_b->PlayBook();
}
void With_RawPointer(void) {
AudioBook * rawptr = new AudioBook("The Hitchhiker's Guide to the Galaxy", "Douglas Adams");
rawptr->PlayBook();
// Make sure that raw pointer (rawptr) is deleted, else it would cause memory leak.
delete rawptr;
}
int main() {
With_UniquePointer();
With_RawPointer();
return 0;
}
Output
Playing : Lord Of The Rings
Playing : Game Of Thrones
Playing : The Hitchhiker's Guide to the Galaxy
| Unique pointer ( std :: unique_ptr ) | Shared pointer ( std :: shared_ptr ) |
|---|---|
| - Unique pointer ( std :: unique_ptr ) does not share the ownership of the underlying raw pointer. - Unique pointer cannot be copied into some other unique pointer, also it cannot be passed by value to a function. A unique_pointer can only be moved. - make_unique function creates and returns a unique_pointer to an object constructed using the provided arguments. - make_unique provides exception safety and is preferred over calling unique_ptr constructors. - Object pointed by unique pointer is destroyed and its memory deallocated when either of the following happens: 1. The unique_ptr object is destroyed. 2. The unique_ptr object is assigned another pointer via operator= or reset(). |
- Shared pointer ( std :: shared_ptr ) shares the ownership of the underlying raw pointer while keeping a count of the references to the dynamically allocated object. - Shared pointer after initialization can be copied into another shared_pointer, and can be passed by value in function arguments. - make_shared function creates and returns a shared_pointer to an object constructed using the provided arguments. - make_shared provides exception safety and is preferred over calling shared_ptr constructors. - Object pointed by shared pointer is destroyed and its memory deallocated when either of the following happens: 1. The last shared_ptr managing the object is destroyed. 2. The last shared_ptr managing the object is assigned another pointer via operator= or reset(). |
Consider the below scenario where an object A ( Google stock object ) has a reference to object B ( Tesla stock object) and vice-versa.
#include <iostream>
#include <memory>
class Stock
{
private:
std :: string stock_name;
std :: shared_ptr<Stock> purchase;
public:
Stock (const std::string& name) : stock_name (name) {
std :: cout << "Stock object [" << stock_name << "] created" << std :: endl;
}
~Stock () {
std :: cout << "Stock object [" << stock_name << "] destroyed" << std :: endl;
}
// The Transact function purchases stock_b with stock_a and vice-versa thereby creating a circular references.
static void Transact (std :: shared_ptr<Stock>& stock_a, std :: shared_ptr<Stock>& stock_b) {
if (stock_a && stock_b) {
stock_a->purchase = stock_b;
stock_b->purchase = stock_a;
std :: cout << "Transaction 1" << std :: endl;
std :: cout << "Stock [" << stock_a->stock_name << "] purchased with stock [" << stock_b->stock_name << "]\n";
std :: cout << "Transaction 2" << std :: endl;
std :: cout << "Stock [" << stock_b->stock_name << "] purchased with stock [" << stock_a->stock_name << "]\n";
}
}
};
int main() {
auto tesla { std :: make_shared<Stock>("Tesla") };
auto google { std :: make_shared<Stock>("Google") };
// Purchase Tesla stock with Google and vice-versa
Stock :: Transact (tesla, google);
return 0;
}
Note : The destructor of the Stock class doesn’t get called even though the shared pointers ( tesla & google ) go out of scope and cause a memory leak.
We notice
2 shared pointers pointing to Tesla object. One being std :: shared_ptr ( tesla ) and the other is Google’s ( purchase )
2 shared pointers pointing to Google object. One being std :: shared_ptr ( google ) and the other is Tesla’s ( purchase )
At the end of the main function, before std :: shared_ptr ( tesla ) goes out of scope it checks if there are any references to Tesla’s object.
Since’s Google’s std :: shared_ptr ( purchase ) still holds a reference on Tesla’s object, tesla’s destructor doesn’t get called hence Tesla’s object remains in memory. The same thing happens to std :: shared_ptr ( google ). google’s destructor doesn’t get called because Tesla’s std :: shared_ptr ( purchase ) has a reference on Google’s object.
Output
Stock object [Tesla] created
Stock object [Google] created
Transaction 1
Stock [Tesla] purchased with stock [Google]
Transaction 2
Stock [Google] purchased with stock [Tesla]
The memory leak caused by a std :: shared_ptr in circular references can be fixed using a weak pointer ( std :: weak_ptr )
Note : std :: weak_ptr does not fix the circular references problem. A design change would be needed to address the circular reference problem (which is beyond the scope of this article).
| Weak pointer ( std :: weak_ptr ) |
|---|
| - Weak pointer ( std :: weak_ptr ) holds a weak ( non - owning ) reference to the dynamically allocated object that is managed by a shared pointer ( std :: shared_ptr ). i.e The weak pointer acts as an observer and can access the object owned by a std :: shared_ptr; but does not own the object. - Weak pointer ( std :: weak_ptr ) cannot be used directly as it does not have -> operator. It has to be converted into a shared pointer ( std :: shared_ptr ) to access the dynamically allocated object. |
Addressing memory leak
When the std :: shared_ptr goes out of scope, it only considers the references held on the dynamically created object that it points to by other std :: shared_ptr and not by std :: weak_ptr. So if ( purchase ) is an std :: weak_ptr, the reference that it holds on the Stock object is not considered when the std :: shared_ptr goes out of scope.
Thus in the above example,
#include<iostream>
#include<memory>
#include<string>
class AudioBook {
private:
std::string title;
std::string author;
public:
AudioBook() {}
AudioBook (std::string arg_title, std::string arg_author): title(arg_title), author(arg_author) {
std :: cout << "Parameterized constructor for AudioBook got called." << std :: endl;
}
inline void PlayBook() {
std::cout << "Playing : " << title << std::endl;
}
~AudioBook() {
std :: cout << "Destructor for AudioBook got called." << std :: endl;
}
};
int main() {
auto uptr_a = std::make_unique<AudioBook>("Lord Of The Rings", "J. R. R. Tolkien");
uptr_a->PlayBook();
auto uptr_b = std::move(uptr_a);
uptr_b->PlayBook();
uptr_b.reset(); // Memory freed as nobody owns it now.
return 0;
}
Output
Parameterized constructor for AudioBook got called.
Playing : Lord Of The Rings
Playing : Lord Of The Rings
Destructor for AudioBook got called.
#include<iostream>
#include<memory>
#include<string>
class AudioBook {
private:
std :: string title;
std :: string author;
public:
AudioBook() {}
AudioBook (std :: string arg_title, std :: string arg_author) : title(arg_title), author(arg_author) {
std :: cout << "Parameterized constructor for AudioBook got called" << std :: endl;
}
inline void PlayBook() {
std::cout << "Playing : " << title << std :: endl;
}
~AudioBook() {
std :: cout << "Destructor for AudioBook got called" << std :: endl;
}
};
int main() {
auto shared_ptr_a = std :: make_shared<AudioBook>("Sherlock Holmes", "Arthur Conan Doyle");
shared_ptr_a->PlayBook();
{
// Initialization via assignment operator increments the reference count.
auto shared_ptr_b = shared_ptr_a;
shared_ptr_b->PlayBook();
// Initialization via copy constructor increments the reference count.
auto shared_ptr_c(shared_ptr_b);
shared_ptr_c->PlayBook();
// When the below shared pointer that is not being shared with others goes out of scope,
// the destructor gets called.
auto shared_ptr_scoped = std :: make_shared<AudioBook>("Rabbit & Bear", "Julian Gough");
shared_ptr_scoped->PlayBook();
}
// Shared pointer can also be initialized with null pointer
auto shared_ptr_d(nullptr);
std :: cout << "Program ends. Returning now" << std :: endl;
return 0;
}
Output
Parameterized constructor for AudioBook got called
Playing : Sherlock Holmes
Playing : Sherlock Holmes
Playing : Sherlock Holmes
Parameterized constructor for AudioBook got called
Playing : Rabbit & Bear
Destructor for AudioBook got called
Program ends. Returning now
Destructor for AudioBook got called
#include <iostream>
#include <memory>
class Stock
{
private:
std :: string stock_name;
// Shared pointer [ std :: shared_ptr<Stock> ] would give rise to circular references, causing a memory leak.
// The memory leak could be fixed by making purchase a weak pointer [ std :: weak_ptr<Stock> ]
std :: weak_ptr<Stock> purchase;
public:
Stock (const std :: string& name) : stock_name (name) {
std :: cout << "Stock object [" << stock_name << "] created" << std :: endl;
}
~Stock () {
std :: cout << "Stock object [" << stock_name << "] destroyed" << std :: endl;
}
// // The Transact function purchases stock_b with stock_a and vice-versa thereby creating a circular references.
static void Transact (std :: shared_ptr<Stock>& stock_a, std :: shared_ptr<Stock>& stock_b) {
if (stock_a && stock_b) {
stock_a->purchase = stock_b;
stock_b->purchase = stock_a;
std :: cout << "Transaction 1" << std :: endl;
std :: cout << "Stock [" << stock_a->stock_name << "] purchased with stock [" << stock_b->stock_name << "]\n";
std :: cout << "Transaction 2" << std :: endl;
std :: cout << "Stock [" << stock_b->stock_name << "] purchased with stock [" << stock_a->stock_name << "]\n";
}
}
};
int main() {
auto tesla { std :: make_shared<Stock>("Tesla") };
auto google { std :: make_shared<Stock>("Google") };
// Purchase Tesla stock with Google and vice-versa
Stock :: Transact (tesla, google);
return 0;
}
Output
Stock object [Tesla] created
Stock object [Google] created
Transaction 1
Stock [Tesla] purchased with stock [Google]
Transaction 2
Stock [Google] purchased with stock [Tesla]
Stock object [Google] destroyed
Stock object [Tesla] destroyed
© 2019-2026 Algotree.org | All rights reserved.
This content is provided for educational purposes. Feel free to learn, practice, and share knowledge.
For questions or contributions, visit algotree.org
"The most disastrous thing that you can ever learn is your first programming language. - Alan Kay"