How to Use Smart Pointers in C++

Catching Memory Allocation Bugs How to Use Smart Pointers in C++

One of the major strengths and weaknesses of both C and C++ are pointers. You can forget to allocate or free memory in the relevant place and program will work perfectly except for unexpected crashes during the most inconvenient time. Modern C++ (standard 2011) has some tricks to help save time and energy during the development process.

The problem of memory leaks, multiple frees, etc. can be solved using some instances which manage memory by themselves. In some sense, it may look like an intelligent garbage collector in C++. The Standard Library provides a few classes helping with memory management without significant overheads.

The most common helper is shared_ptr. It can hide pointer to an object and count references to this object. Shared pointer automatically calls [cpp]delete[/cpp] statement or a given destructor, when the reference count reaches zero no matter whether it happens in the end of a function, in an exception or somewhere else. Let’s consider the following example.

[cpp]#include <memory>
int main() {
	std::shared_ptr sp = std::make_shared(45);
	std::cout << "sp pointer is used " << sp.use_count() << " times" << std::endl;
	std::shared_ptr sp2 = sp;
	std::cout << "Now sp pointer is used " << sp.use_count() << " times" << std::endl;
	return 0;
} [/cpp]

In the first line of the [cpp]main()[/cpp] function, we create a new shared_ptr object which points to an integer value 45. Function [cpp]make_shared<T>()[/cpp] takes arguments of the constructor and, using [cpp]::new[/cpp] statement inside it, creates a new shared_ptr object with one link to the internal integer object. Next, we declare the second shared pointer: [cpp]sp2 = sp[/cpp]. It results not in a new memory allocation, but in the increment of the reference counter. Let’s check this program:

expert1@AE ~/work/ptr $ g++ test.cpp -Wall -pedantic -std=c++11
expert1@AE ~/work/ptr $ valgrind --leak-check=full ./a.out 
==3068== Memcheck, a memory error detector
==3068== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==3068== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==3068== Command: ./a.out
==3068== 
sp pointer is used 1 times
Now sp pointer is used 2 times
==3068== 
==3068== HEAP SUMMARY:
==3068==     in use at exit: 0 bytes in 0 blocks
==3068==   total heap usage: 1 allocs, 1 frees, 32 bytes allocated
==3068== 
==3068== All heap blocks were freed -- no leaks are possible
==3068== 
==3068== For counts of detected and suppressed errors, rerun with: -v
==3068== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Note that 32 bytes against 4 in the case of raw integer pointers are allocated here. However, we have not used [cpp]delete[/cpp] statement directly and no memory leaks have been detected. Objects of shared_ptr type can be used as typical pointers. They support dereferencing operations for object [cpp]*[/cpp] and members [cpp]->[/cpp]. It can be used in the same way in comparisons which determine whether it is null or not. Note, that shared_ptrs should not be used in function stacks and mixing of typical and smart pointers is a bad style, which may significantly reduce benefits of the latter.

We used a special function make_shared in the example above instead of the shared_ptr constructor:

[cpp]std::shared_ptr<int> sp(new int(45))[/cpp]

It saves time as, obviously, using a constructor, we needs two memory allocations instead of one. Also, it is considered a good programming style, because the new “dangerous”  construction is not used in code with smart pointers at all.

Sometimes one needs only to hold a reference to an object without access. In this situation, weak_ptr template class can be used. It only tracks object state and should be converted to shared_ptr for practical use of an object. Weak pointers may be used when there are two classes storing references to each other. Without weak_ptr, a circular loop may appear because shared pointers can prevent the destruction of their instances. In simpler words, object managed by shared_ptr will be deleted only if both numbers of shared and weak pointers count reaches zero.

The last important smart pointer type is unique_ptr. It allows to have only one reference to an object and costs nothing to use. Unique pointers are like a wrap for an object, which can be demonstrated with the following example:

[cpp]#include <iostream>
#include <memory>
int main() {
	std::unique_ptr<int> up(new int(45));
	std::cout << "up contains the following data: " << *up << std::endl;
	std::unique_ptr<int> up2(std::move(up));
	std::cout << "up2 contains the following data: " << *up2 << std::endl;
	if (up) {
		std::cout << "up now contains the following data: " << *up << std::endl;
	}
	else {
		std::cout << "up is empty" << std::endl;
	}
	return 0;
}
[/cpp]

Here we create a new unique pointer [cpp]up[/cpp] with integer data and display it. Then we create another unique pointer [cpp]up2[/cpp] and move data to it. The last decision-making statement checks whether [cpp]up[/cpp] still contains something or it is a [cpp]nullptr[/cpp] already. Take a look at the output:

expert1@AE ~/work/ptr $ g++ test.cpp -Wall -pedantic -std=c++11
 expert1@AE ~/work/ptr $ valgrind --leak-check=full ./a.out
 ==3945== Memcheck, a memory error detector
 ==3945== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
 ==3945== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
 ==3945== Command: ./a.out
 ==3945==
 up contains the following data: 45
 up2 contains the following data: 45
 up is empty
 ==3945==
 ==3945== HEAP SUMMARY:
 ==3945== in use at exit: 0 bytes in 0 blocks
 ==3945== total heap usage: 1 allocs, 1 frees, 4 bytes allocated
 ==3945==
 ==3945== All heap blocks were freed -- no leaks are possible
 ==3945==
 ==3945== For counts of detected and suppressed errors, rerun with: -v
 ==3945== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

In fact, [cpp]up[/cpp] is empty after moving pointer to [cpp]up2[/cpp]. Pay attention that only 4 bytes is allocated and freed here similarly to when a raw integer pointer is used in the program.

Thus, modern C++ tools allow to effectively manage memory in programs without dangerous leaks in a simple way using C++ 2011 standard features: shared_ptr, weak_ptr and unique_ptr. If you still allocate and free memory manually, try these methods also. We also recommend this tutorial to get more examples.

Filed under Programming.
0 0 votes
Article Rating
guest
0 Comments
Inline Feedbacks
View all comments