ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


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

More in detail, a weak pointer is associated to a shared pointer. Whenever someone needs to access the contents of a weak pointer, the weak pointer must first be converted to a shared pointer by means of the lock method or a special constructor. If the object managed by the shared pointer goes away, any further conversion from the weak pointer to a shared pointer will fail.

Here is a fictitious example in which the use of a weak pointer might be useful. A shared pointer is accessed by two threads. thread1 should be able to release the object, but the object should be accessible at some point in thread2 only if it still exists. Using the weak pointer makes this situation possible without using a complex locking protocol:

boost::shared_ptr<int> ptri(new int(5));

void
thread1(void* arg)
{
    ...

    ptri.reset();

    ...
}

void
thread2(void* arg)
{
    boost::weak_ptr<int> wptr(ptri);

    ...

    if (boost::shared_ptr<int> auxptr = ptri.lock()) {
        // Do something with 'auxptr' because the object is
        // still available.
    }

    ...
}

Intrusive pointers

The intrusive pointer is a light version of the shared pointer. It assumes that the dynamically allocated object it has to point to implements a reference counter by itself. This maintains the counter in a single place and keeps the smart pointer to the minimum size (the size of the raw pointer).

In order to define an intrusive pointer for a given object, you must first create two functions that manage its embedded reference counter. One is intrusive_ptr_add_ref, which simply increases the counter; the other is intrusive_ptr_release, which decreases the counter and, if it reaches zero, releases the object.

This is much clearer with an example. You may want to assume that some_resource is a class you have written or a class provided by someone else, perhaps even the operating system itself.

#include <cstdlib>
#include <iostream>

#include <boost/intrusive_ptr.hpp>

class some_resource
{
    size_t m_counter;

public:
    some_resource(void) :
        m_counter(0)
    {
        std::cout << "Resource created" << std::endl;
    }

    ~some_resource(void)
    {
        std::cout << "Resource destroyed" << std::endl;
    }

    size_t refcnt(void)
    {
        return m_counter;
    }

    void ref(void)
    {
        m_counter++;
    }

    void unref(void)
    {
        m_counter--;
    }
};

void
intrusive_ptr_add_ref(some_resource* r)
{
    r->ref();
    std::cout << "Resource referenced: " << r->refcnt()
              << std::endl;
}

void
intrusive_ptr_release(some_resource* r)
{
    r->unref();
    std::cout << "Resource unreferenced: " << r->refcnt()
              << std::endl;
    if (r->refcnt() == 0)
        delete r;
}

int
main(void)
{
    boost::intrusive_ptr<some_resource> r(new some_resource);
    boost::intrusive_ptr<some_resource> r2(r);

    std::cout << "Program exiting" << std::endl;

    return EXIT_SUCCESS;
}

Conclusion

I discovered smart pointers a year ago, and since then I firmly believe that their use makes code safer, more robust, and easier to read. I can no longer conceive any C++ code that directly manages dynamically allocated objects due to all the problems that can arise.

Now that you have finished this article, I hope you feel similarly and that from now on you will use these techniques to improve your own programs.

Time to code!

Julio M. Merino Vidal studies computer science at the FIB faculty in Barcelona, Spain.


Return to the ONLamp.com.



Sponsored by: