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;
}
I always think that questions like this should have at least one answer quote from the standard for future readers, so here it is.
§12.8.4 of the standard states that:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly.
This implies that when a class definition does explicitly declare a copy constructor, one is not declared implicitly. So if you declare one explicitly, the implicit one does not exist, so you can't call it.
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
the default constructor leaves a
, b
, c
, d
and e
uninitialized, and
the code does not take charge of assignment copying, and
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With