Namespaces
Variants
Actions

intro/smart pointers

From cppreference.com
Revision as of 02:38, 9 January 2014 by Paul.kertscher (Talk | contribs)

Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced).

See this simple example:

void my_func()
{
    int* valuePtr = new int(15);
    int x = 45;
    // ...
    if (x == 45)
        return;   // here we have a memory leak, valuePtr is not deleted
    // ...
    delete valuePtr;
}
 
int main()
{
}


The same example using the unique_ptr<> template:

#include <memory>
 
void my_func()
{
    std::unique_ptr<int> valuePtr(new int(15));
    int x = 45;
    // ...
    if (x == 45)
        return;   // no memory leak anymore!
    // ...
}
 
int main()
{
}


The unique_ptr<> template holds a pointer to an object and deletes this object when the unique_ptr<> object is deleted. So, in the example above, it does not matter if the function scope is left through the return statement, at the end of the function or even through an exception: The unique_ptr<> destructor is always called and therefore the object (int in the example) always deleted.

The following three smart pointer templates are available:

Contents

unique_ptr

As the name implies, make sure that only exactly one copy of an object exists. Can be used as in the example above for handling dynamically allocated objects in a restricted scope.

A unique pointer can be initialized with a pointer upon creation

std::unique_ptr<int> valuePtr(new int(47));

or it can be created without a pointer and assigned one later

std::unique_ptr<int> valuePtr; 
valuePtr.reset(new int(47));

Note: In this second case, if the unique_ptr<> already holds a pointer to an existing object, this object is deleted first and then the new pointer stored.

Afterwards, an object managed by a unique_ptr<> can be accessed just like when you would use a raw pointer:

std::unique_ptr<std::string>  strPtr(new std::string);   strPtr->assign("Hello world");

The unique_ptr<> does not support copying. If you try to copy a unique_ptr<>, you'll get compiler errors. However, it supports move semantics, where the pointer is moved from one unique_ptr<> to another, which invalidates the first unique_ptr<>.

See the following example:

#include <iostream>
#include <memory>
#include <utility>
 
int main()
{
    std::unique_ptr<int> valuePtr(new int(15));
    std::unique_ptr<int> valuePtrNow(std::move(valuePtr));
 
    std::cout << "valuePtrNow = " << *valuePtrNow << '\n';
    std::cout << "Has valuePtr an associated object? "
              << std::boolalpha
              << static_cast<bool>(valuePtr) << '\n';
}

Output:

valuePtrNow = 15
Has valuePtr an associated object? false

shared_ptr

The shared_pointer is a reference counting smart pointer that can be used to store and pass a reference beyond the scope of a function. This is particularly useful in the context of OOP, to store a pointer as a member variable and return it to access the referenced value outside the scope of the class. Consider the following example:

#include <memory>
 
class Foo
{
	public void doSomething();
};
 
class Bar
{
private:
	std::shared_ptr<Foo> pFoo;
public:
	Bar()
	{
		pFoo = std::shared_ptr<Foo>(new Foo());
	}
 
	std::shared_ptr<Foo> getFoo()
	{
		return pFoo;
	}
};


When an object of the Bar class is created it creates a new object of the Foo class, which is stored in pFoo. To Access pFoo we can call Bar::getFoo which returns a std::shared_ptr to the Foo object created in the Bar constructor. Internally, a copy of the std::shared_ptr object is created and returned. The copy constructor of std::shared_ptr copies the internal pointer to the Foo object and increases the reference count. This would, for example, happen in the following example:

void SomeAction()
{
	Bar* pBar = new Bar(); //with the Bar object, a new Foo is created and stored
	//reference counter = 1
 
	std::shared_ptr<Foo> pFoo = pBar->getFoo(); //a copy of the shared pointer is created
	//reference counter = 2
 
	pFoo->doSomething(); 
 
	delete pBar; //with pBar the private pFoo is destroyed
	//reference conter = 1
 
	return; //with the return the local pFoo is destroyed automatically 
	//reference counter = 0
	//internally the std::shared_ptr destroys the reference to the Foo object
}


So there's no need for Bar to care about deleting pFoo, which increases memory management severely.

void SomeOtherAction(std::shared_ptr<Bar> pBar)
{
	std::shared_ptr<Foo> pFoo = pBar->getFoo(); //a copy of the shared pointer is created
	//reference counter = 2
 
	pFoo->doSomething(); 
 
	return; //local pFoo is destroyed
	//reference counter = 1
}


When the function returns, pBar is deleted, but there is still a copy of the std::shared_ptr outside the scope of the function, therefor the internal Bar object will not be destroyed, which sustains the reference to the Foo object, which is in turn not destroyed either.

With smart_ptr it is easily possible passing and returning references to objects without rising to run into memory leaks or access deleted references. Thus they are a really powerful feature of modern memory management.

weak_ptr

auto_ptr

The auto_ptr<> was the first implementation of a smart pointer in the standard library. However, when the new C++ standard (C++11) was defined, it was replaced by the unique_ptr template due to some design flaws and new features of the language. Since this is a tutorial, the auto_ptr<> is not explained here. Use the new, improved smart pointers described above.