firebush
firebush

Reputation: 5850

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?

Upvotes: 1

Views: 285

Answers (1)

nosid
nosid

Reputation: 50044

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.

Upvotes: 1

Related Questions