Search code examples
c++constructorshared-ptrcopy-constructordeep-copy

Call default copy constructor from within overloaded copy constructor


I need to write a copy constructor that deep copies the contents of a std::shared_ptr. However, there are a bunch of variable int a, b, c, d, e; also defined in the class. Is there a way to generate the default copy constructor code (or call the default copy constructor) inside my new overloaded one.

Here is a code snippet with a comment that hopefully clarifies the issue.

class Foo {
public:
     Foo() {}
     Foo(Foo const & other);
     ...
private:
     int a, b, c, d, e;
     std::shared_ptr<Bla> p;
};

Foo::Foo(Foo const & other) {
    p.reset(new Bla(*other.p));

    // Can I avoid having to write the default copy constructor code below
    a = other.a;
    b = other.b;
    c = other.c;
    d = other.d;
    e = other.e;
}

Solution

  • Here’s the question code as I’m writing this:

    class Foo {
    public:
         Foo() {}
         Foo(Foo const & other);
         ...
    private:
         int a, b, c, d, e;
         std::shared_ptr<Bla> p;
    };
    
    Foo::Foo(Foo const & other) {
        p.reset(new Bla(other.p));
    
        // Can I avoid having to write the default copy constructor code below
        a = other.a;
        b = other.b;
        c = other.c;
        d = other.d;
        e = other.e;
    }
    

    The above code is most likely wrong, because

    1. the default constructor leaves a, b, c, d and e uninitialized, and

    2. the code does not take charge of assignment copying, and

    3. the expression new Bla(other.p) requires that Bla has a constructor taking a std::shared_ptr<Bla>, which is extremely unlikely.

    With std::shared_ptr this would have to be C++11 code in order to be formally correct language-wise. However, I believe that it’s just code that uses what’s available with your compiler. And so I believe that the relevant C++ standard is C++98, with the technical corrections of the C++03 amendment.

    You can easily leverage the built-in (generated) copy initialization, even in C++98, e.g.

    namespace detail {
        struct AutoClonedBla {
            std::shared_ptr<Bla> p;
    
            AutoClonedBla( Bla* pNew ): p( pNew ) {}
    
            AutoClonedBla( AutoClonedBla const& other )
                : p( new Bla( *other.p ) )
            {}
    
            void swap( AutoClonedBla& other )
            {
                using std::swap;
                swap( p, other.p );
            }
    
            AutoClonedBla& operator=( AutoClonedBla other )
            {
                other.swap( *this );
                return *this;
            }
        };
    }
    
    class Foo {
    public:
         Foo(): a(), b(), c(), d(), e(), autoP( new Bla ) {}
         // Copy constructor generated by compiler, OK.
    
    private:
         int                      a, b, c, d, e;
         detail::AutoClonedBla    autoP;
    };
    

    Note that this code does initialize correctly in the default constructor, does take charge of copy assignment (employing the swap idiom for that), and does not require a special smart-pointer-aware Bla constructor, but instead just uses the ordinary Bla copy constructor to copy.