ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


Smart Pointers in C++
Pages: 1, 2, 3, 4, 5

Smart Pointers in Boost

Due to the inherent limitations of the standard auto_ptr class, several third-party C++ libraries provide enhanced smart pointers. Boost is no exception. For those who do not know it, Boost is a collection of free, portable, peer-reviewed C++ libraries. These libraries work well with the standard C++ library, following its style and design principles. Some of them may eventually become part of the C++ standard.



The Boost Smart Pointers library provides six smart pointer templates in three groups: scoped pointers, shared pointers, and intrusive pointers.

Given that the official documentation for the Boost Smart Pointers library is very informative and complete, I have focused the rest of this article on presenting each available smart pointer accompanied by some code examples. If you need to learn more about them, I encourage you to read the official information.

Scoped pointers

A scoped pointer owns a dynamically allocated object, much like the standard auto_ptr does. The difference lies in that the scoped pointer is explicitly marked as noncopyable, ensuring that during compilation time the pointer is never copied. It can be seen as a way to give a casual reader of your code a signal that you do not intend for that specific object to leave the local scope (hence the name "scoped pointer").

Boost provides two scoped pointers: scoped_ptr and scoped_array. The former is good for raw pointers, while the latter is useful for dynamic arrays (those allocated with new[]) because they are treated in a slightly different manner. These two smart pointers are extremely lightweight: they do not occupy more space than a raw pointer, and the most common operations are as cheap as the access to a raw pointer.

Here is an example to illustrate the usage of both classes by dynamically allocating a test class and letting its instances go out of scope. The printed messages show how the code properly releases the memory before exiting:

#include <cstdlib>
#include <iostream>

#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>

static int count = 0;

class printer
{
    int m_id;

public:
    printer(void) :
        m_id(count++)
    {
    }

    ~printer(void)
    {
        std::cout << "Printer " << m_id
                  << " destroyed" << std::endl;
    }
};

int
main(void)
{
    boost::scoped_ptr<printer> p1(new printer);
    boost::scoped_array<printer> p2(new printer[5]);

    std::cout << "Exiting test program" << std::endl;

    return EXIT_SUCCESS;
}

Shared pointers

A shared pointer is something you cannot live without once you've learned what it is. If smart pointers are great by themselves, imagine how incredible these are! Really. Remember the essay about garbage collectors at the beginning of the article? Shared pointers are the alternative I was aiming at.

More seriously, a shared pointer owns a reference to a dynamically allocated pointer--as all other smart pointers do--but does not own the object itself. This subtle difference is very important: a shared pointer counts the number of users of the dynamic object by means of a reference counter. The dynamic object is released only when the last shared pointer referencing it disappears.

You can copy and transfer shared pointers in a trivial way, yet these operations are completely safe: they do not duplicate memory nor try to release a single chunk earlier than necessary. Even more, they are also useful to write clearer code. By using a shared pointer, you no longer need to identify who is responsible for freeing a returned object nor which rules apply to an input parameter. This certainly helps when designing a public API.

Boost provides two shared pointers: shared_ptr and shared_array. The former is used for raw pointers, while the latter is used for dynamic arrays (those allocated with new[]), for the same reasons as scoped pointers.

Consider a simple example:

#include <cstdio>
#include <iostream>

#include <boost/shared_array.hpp>

class useless_buffer
{
    size_t m_length;
    boost::shared_array<char> m_buffer;

public:
    useless_buffer(const std::string& str) :
        m_length(str.length() * 2),
        m_buffer(new char[m_length])
    {
        std::strcpy(m_buffer.get(), str.c_str());
    }

    boost::shared_array<char> get_buffer(void)
    {
        return m_buffer;
    }

    boost::shared_array<char> copy_buffer(void)
    {
        boost::shared_array<char> copy(new char[m_length]);
        std::memcpy(copy.get(), m_buffer.get(), m_length);
        return copy;
    }
};

int
main(void)
{
    useless_buffer buf("Hello, world!");

    std::cout << buf.get_buffer().get() << std::endl;
    std::cout << buf.copy_buffer().get() << std::endl;

    return EXIT_SUCCESS;
}

It is interesting to note in this example that from the caller's point of view, there is absolutely no visible difference between the get_buffer and copy_buffer methods. The former returns a new reference for a local array, which the caller must not release. The latter returns a new dynamic object, which the caller must release.

As a complement to shared pointers, Boost provides the weak_ptr smart pointer. This one does not own an object but instead holds a "weak reference" to an existing shared pointer. The shared pointer can change at will regardless of how many weak pointers are "watching" it.

Pages: 1, 2, 3, 4, 5

Next Pagearrow





Sponsored by: