Search code examples
boostshared-ptrcopy-constructor

boost::shared_* with copy constructor and assignment operator


I have a class that contains a boost::shared_array member. The other members are not dynamic - just a bunch of ints, no pointers. I would expect that the default copy constructor for such a class would be fine.

This is my assumption:

  1. Let's say I have an instance of this class, orig.

  2. orig's shared_array member has a reference count of 1.

  3. Now I create a copy of orig:

    copy = orig;

  4. I now expect both copy and orig to have shared_arrays that point to the same underlying memory, each with a reference count of 2.

Is the above correct?

I'm intimidated by various people who warn against the default copy constructor when there is a boost::shared_* member - but I can never find an explanation why the default would/could be bad. For example, here's a comment by someone who says an explicit copy/assignment should be defined, but no explanation why:

https://stackoverflow.com/a/716112/629530

Can someone clarify when a copy constructor and assignment operator need to be defined for a class that contains boost::shared_* (shared_array and shared_ptr) members?


Solution

  • The following class uses the Pimpl Idiom in combination with a shared_ptr:

    class location
    {
        struct impl
        {
            double _latitude;
            double _longitude;
        };
        std::shared_ptr<impl> _impl;
    public:
        location(double latitude, double longitude)
            : _impl{new impl{latitude, longitude}}
        { }
        void move_to(double latitude, double longitude)
        {
            _impl->_latitude = latitude;
            _impl->_longitude = longitude;
        }
        // ...
    };
    

    The code compiles and works. However, there is a strange behaviour:

    location london{51.51, 0.12};
    location paris = london;
    paris.move_to(48.86, 2.35);
    std::cout << "London: " << london << '\n'; // prints 48.86/2.35
    

    Changing the copy of an object also affected the original object. In this case, it is better to use std::unique_ptr instead of std::shared_ptr, because we would have been forced to write our own copy constructor.

    There are also cases in which the behaviour of std::shared_ptr is desired. Let's add a member variable metric that will be used to calculate the distance between two locations (because there might be different strategies for calculating distances):

        std::shared_ptr<metric> _metric;
        double operator-(const location& rhs) const
        {
            return _metric->distance(*_impl, *rhs->_impl);
        }
    

    In this case a std::shared_ptr works perfectly, because the metric is not part of the perceived state of the location, and there is no way to change the metric.