0% found this document useful (0 votes)
48 views

Note 12 24 SmartPointer

Dynamic memory in C++ is managed using new and delete operators. new allocates memory and returns a pointer, while delete frees the memory. Issues arise from forgetting to delete memory, using deleted memory, and deleting the same memory twice. Smart pointers like shared_ptr and unique_ptr avoid these issues by automatically freeing memory when the pointer goes out of scope. Shared pointers use reference counting to track multiple pointers to an object and free it when the count reaches zero.
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views

Note 12 24 SmartPointer

Dynamic memory in C++ is managed using new and delete operators. new allocates memory and returns a pointer, while delete frees the memory. Issues arise from forgetting to delete memory, using deleted memory, and deleting the same memory twice. Smart pointers like shared_ptr and unique_ptr avoid these issues by automatically freeing memory when the pointer goes out of scope. Shared pointers use reference counting to track multiple pointers to an object and free it when the count reaches zero.
Copyright
© © All Rights Reserved
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 9

Object-Oriented Programming Language

12/24/2014

Chapter 12: Dynamic Memory


12.1. Dynamic Memory and Smart Pointers
In its most elementary form, C++ dynamic memory is managed through a pair of operators:
- new, which allocates, and optionally initializes, an object in dynamic memory and returns
a pointer to that object
- delete, which takes a pointer to a dynamic object, destroys that object, and frees the
associated memory.
int * p1 = new int;
*p1 = 1001;

int * p2 = new int(1001); // *p2 = 1001

int * p3 = new int(); // value initialized to 0; *p3=0

delete p1;

delete p2;

delete p3;

The new operator computes the size of the requested memory. In this case, it takes an integer,
and it returns enough memory to hold an integer value.
int * p1 = new int;
*p1 = 1001;
delete p1;
p1 is set to point to that memory and now p1 and the associated code that uses it become the

owner of that memoryin other words, the code that uses p1 must eventually return this
memory back to the free store, an operation called freeing the memory. Until p1 is freed, the
memory that is pointed to will be marked as in-use and will not be given out again. If you
keep allocating memory and never free it, you will run out of memory. The delete operation
frees up the memory allocated through new.
Before C++ introduces STL, dynamic memory via the mechanism of new and delete is the
only way to create an array dynamically (e.g., std::vector<T>). You can dynamically
allocate an array of memory using new and assign that memory to a pointer:
1

Object-Oriented Programming Language


12/24/2014
int *p_numbers = new int[8];

Using the array syntax as the argument to new tells the compiler how much memory it needs
enough for an 8 element integer array. Now you can use p_numbers just as if it pointed to
an array. Unlike built-in arrays, though, you need to free the memory pointed to by
p_numbers. To free the memory, there is a special syntax for the delete operator:
delete[] p_numbers;

The brackets tell the compiler that the pointer points to an array of values, rather than a single
value.
In-class Exercise 12.1: write a program to dynamically allocate the requested memory for a
string array via new, change all the values to C++ and output the contents. Below is a
sample run:

A:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Enter the size of the string array: ";
int num;
cin >> num;
string* parr = new string[num];
for (unsigned i = 0; i < num; ++i)
parr[i] = "C++";
cout << endl;
cout << "The new contents of the string array are: " << endl;
for (unsigned i = 0; i < num; ++i)
cout << parr[i] << " ";
cout << endl;
delete [] parr;
return 0;
}

Dynamic memory is problematic because it is surprisingly hard and error-prone. There are
2

Object-Oriented Programming Language


12/24/2014
three common problems with using new and delete to manage dynamic memory:
1. Forgetting to delete memory. Neglecting to delete dynamic memory is known as a
memory leak, because the memory is never returned to the free store. Testing for
memory leaks is difficult because they usually cannot be detected until the application is
run for a long enough time to actually exhaust memory.
2. Using an object after it has been deleted. This error can sometimes be detected by
making the pointer null after the delete.
delete p1;
p1 = nullptr; // or p1 = 0; p1 = NULL;

By setting the pointer to nullptr, if your code does try to dereference the pointer after it is
freed, you will find out immediately because the program will crash right away.
3. Deleting the same memory twice. This error can happen when two pointers point to the
same dynamically allocated object. If delete is applied to one of the pointers, then the
objects memory is returned to the free store. If we subsequently delete the second
pointer, then the free store may be corrupted.
These kinds of errors are considerably easier to make than they are to find and fix (VERY
BAD).
A usual approach to avoid these kinds of problems is to use smart pointers. A smart
pointer is a template class object that acts like a pointer but has additional features. They are
smart in the sense that they support programmers in avoiding problems such as those just
described. For example, a smart pointer can be so smart that it knows whether it is the last
pointer to an object and uses this knowledge to delete an associated object only when it, as
last owner of an object, gets destroyed.
It is not sufficient to provide only one smart pointer class. Smart pointers can be smart about
different aspects. Since C++11, the C++ standard library provides two major types of smart
pointer:
1. Class shared_ptr for a pointer that implements the concept of shared ownership.
Multiple smart pointers can refer to the same object so that the object and its associated
resources get released whenever the last reference to it gets destroyed.
2. Class unique_ptr for a pointer that implements the concept of exclusive ownership or
3

Object-Oriented Programming Language


12/24/2014
strict ownership. This pointer ensures that only one smart pointer can refer to this object
at a time. However, you can transfer ownership. This pointer is especially useful for
avoiding resource leaks, such as missing calls of delete after or while an object gets
created with new and an exception occurred.
You might wonder why the C++ standard library not JUST only provides shared_ptr
because shared_ptr also avoids resource leaks. The answer has to do with the
performance impact of shared_ptr. The performance price is that a shared_ptr object
internally needs multiple members: an ordinary pointer to the referenced object and a
reference counter shared by all shared pointers that refer to the same object.
Remark: Historically, C++98 had only one smart pointer class provided by the C++ standard
library, class auto_ptr<>, which was designed to perform the task that unique_ptr now
provides. auto_ptr officially became deprecated (DONT USE IT ANYMORE) with C+
+11 after class shared_ptr was introduced with TR1 and class unique_ptr was
introduced with C++11.
All smart pointer classes are defined in the <memory> header file. We will only cover a few
aspects of shared_ptr in our course.
12.1.1 The shared_ptr Class
Like vectors, smart pointers are class templates. Therefore, when we create a smart
pointer, we must supply the type to which the pointer can point.
shared_ptr<string> p1; // shared_ptr that can point at a string
shared_ptr<list<int>> p2; // shared_ptr that can point at a list of ints

We use a smart pointer in ways that are similar to using a pointer (a smart pointer is a
template class object that acts like a pointer but has additional features). Dereferencing a
smart pointer returns the object to which the pointer points. When we use a smart pointer in a
condition, the effect is to test whether the pointer is null:
// if p1 is not null, check whether it's the empty string
shared_ptr<string> p1; // shared_ptr that can point at a string
if (p1 && p1->empty())
*p1 = "hi"; // if so, dereference p1 to assign a new value to that string

If we do not initialize a smart pointer, it is initialized as a null pointer. We can initialize a


smart pointer from a pointer returned by new with direct form of initialization:

Object-Oriented Programming Language


12/24/2014
shared_ptr<int> p1; // shared_ptr points at an int
shared_ptr<int> p2(new int(42)); // shared_ptr points at an int

Note that because the constructor from the class shared_ptr taking a pointer as single
argument is explicit, you CANNOT use the assignment notation here because that is
considered to be an implicit conversion.
shared_ptr<int> p2 = new int(42); // ERROR

The make_shared Function


The safest way to allocate and use dynamic memory is to call a library function named
make_shared. This function allocates and initializes an object in dynamic memory and
returns a shared_ptr that points to that object.
// shared_ptr that points to an int with value 42
shared_ptr<int> p3 = make_shared<int>(42);
// p4 points to a string with value 9999999999
shared_ptr<string> p4 = make_shared<string>(10, '9');
// p5 points to a dynamically allocated, empty vector<string>
auto p5 = make_shared<vector<string>>();

Q: Make a shared_ptr that points to a list of ints initialized with 20 elements equal to
9.
A:
auto p = make_shared<list<int>>(20, 9);

Copying and Assigning shared_ptrs


When we copy or assign a shared_ptr, each shared_ptr keeps track of how many other
shared_ptrs point to the same object
auto p = make_shared<int>(42); // object to which p points has one user
auto q(p); // p and q point to the same object
// object to which p and q point has two users

This is known as a reference count or user count. Whenever we copy a shared_ptr, the
count is incremented. Once a shared_ptrs counter goes to zero, the shared_ptr
automatically (thus the smart pointer) frees the object that it manages:
auto p = make_shared<int>(42); // object to which p points has one user
auto q(p); // p and q point to the same object
5

Object-Oriented Programming Language


12/24/2014
auto r = make_shared<int>(); // int to which r points has one
user
r = q;

Q: what are the user count for shared_ptr<int> p? what happens to the
shared_ptr<int> r?
A:
3 user count
r is the only shared_ptr pointing to the one we previously
allocated. That int is automatically freed as part of assigning q
to r.

We can trace the user count through shared_ptr member function use_count(). Notice
that this member function is primary for debugging/learning only and may be a slow
operation.
(Example)
#include <iostream>
#include <memory>
using namespace std;
int main()
{
auto p = make_shared<int>(42); // object to which p points
has one user
cout << "p" << endl;
cout << "Use count: " << p.use_count() << endl;
auto q(p); // p and q point to the same object
cout << "p q" << endl;
cout << "Use count: " << p.use_count() << endl;
cout << "Use count: " << q.use_count() << endl;
auto r = make_shared<int>(); // int to which r points has one
user
cout << "r" << endl;
cout << "Use count: " << r.use_count() << endl;
r = q;
cout << "p q r" << endl;
cout << "Use count: " << p.use_count() << endl;
cout << "Use count: " << q.use_count() << endl;
cout << "Use count: " << r.use_count() << endl;
return 0;
}

Object-Oriented Programming Language


12/24/2014

The fact that the shared_ptr class automatically frees dynamic objects when they are no
longer needed makes it fairly easy to use dynamic memory. For example, we might have a
function that returns a shared_ptr to a dynamically allocated object of a type named Foo
that can be initialized by an argument of type T:
// factory returns a shared_ptr pointing to a dynamically allocated
object

shared_ptr<Foo> factory(T arg)


{
// process arg as appropriate
// shared_ptr will take care of deleting this memory
return make_shared<Foo>(arg);
}

In contrast, we will have a hard time to manage dynamic objects with new:
// factory returns a shared_ptr pointing to a dynamically allocated
object

Foo* factory(T arg)


{
// process arg as appropriate
return new Foo(arg); // caller is responsible for deleting this
memory

(see ppt for more operations specific to shared_ptr)


Smart Pointer Semantic for STL Containers
In general, STL container classes provide value semantics. Thus, the containers create
internal copies of the elements they contain. You cannot directly use reference semantic for
STL containers:
string BIG = string(10000000000000000000);
vector<string&> v; //error, impossible
7

Object-Oriented Programming Language


12/24/2014
If you want the effect of reference semantics in STL containers whether because copying
elements is expensive or because identical elements will be shared by different collections
you should use a smart pointer class that avoids possible errors.
(Example)
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
int main()
{
// two shared pointers representing two persons by their name
shared_ptr<string> pNico(new string("nico"));
auto pJutta = make_shared<string>("jutta");
// capitalize person names
(*pNico)[0] = 'N';
pJutta->replace(0,1,"J");
// put them multiple times in a container
vector<shared_ptr<string>> whoMadeCoffee;
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
// print all elements
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
// overwrite a name again
*pNico = "Nicolai";
// print all elements again
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
// print some internal data
cout << "use_count: " << whoMadeCoffee[0].use_count() <<
endl;
// delete some counts
whoMadeCoffee[1] = pNico;
cout << "use_count: " << whoMadeCoffee[0].use_count() << endl;
}
8

Object-Oriented Programming Language


12/24/2014
Q: what is the output?
A:
Jutta Jutta Nico Jutta Nico
Jutta Jutta Nicolai Jutta Nicolai
use_count: 4
use_count: 3

Remark: At the end of the program, when the last owner of a string gets destroyed, the
shared pointer automatically calls delete for the object it refers to.

You might also like