Here’s the question code as I’m writing this.
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 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; be = 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, 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 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 ); } }; } 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. Cheers & hth.
The Bla copy constructor is not intended to take a shared_ptr. Good catch. The answer still stands, and is very good, thank you.
– Lex Fridman Sep 4 at 15:25.
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.
2 No, that quote from the standard doesn't imply that. – wilhelmtell Sep 4 at 2:44 @wilhelm why not? If the if of any statement is not satisfied, the then isn't true.
Even if that generality is wrong, this particular application of it seems correct. I've never been wrong before though. – Seth Carnegie Sep 4 at 2:46 "If not A then B" does not imply "if A then not B".
But I know what you mean and in this case "if A then not B" is also true. – Lex Fridman Sep 4 at 2:48 @Lex well I was thinking about English. For instance, if your mom says to you "If you're good I'll take you to the toy store" you can assume that if you are bad she will not take you to the toy store.
– Seth Carnegie Sep 4 at 2:50 1 Yes, that's a good guess on your part (in terms of Mom), but it's not a guarantee, and the standard is all about defining guarantees. – Lex Fridman Sep 4 at 2:53.
It would be easier to write a variation on shared_ptr that has deep copying built into it. That way, you don't have to write a copy constructor for your main class; just for this special deep_copy_shared_ptr type. Your deep_copy_shared_ptr would have a copy constructor, and it would store a shared_ptr itself.It could even have an implicit conversion to shared_ptr, to make it a bit easier to use.
1 +1 for pointing out a solution to the OP's question. I was just slightly surprised that this answer had 0 votes (before mine), and was not the one marked as solution. – Alf P.
Steinbach Sep 4 at 6:18.
Not to my knowledge, but what you can (and should) do is use an initializer list, anyway: Foo::Foo(Foo const & other) : a(other. A), b(other. B), c(other.
C), d(other. D), e(other. E), p(new Bla(other.
P)) {} This won't save you from the writing, but it will save you from the possible performance penalty of assigning (unneccessarily) default-constructed members (although in this case it might be fine) and the many other pitfalls this could bring. Always use initializer lists in constructors, if possible. And by the way, Kerrek's comment is right.
Why do you need a shared_ptr, if you make a deep copy anyway. In this case a unique_ptr might be more appropriate. Besides it's benefits shared_ptr is not a general no-more-to-think-about-deallocation solution and you should always think if you need a smart pointer and what type of smart pointer is most appropriate.
1 Generally speaking it's not a good idea to allocate (dynamic) resources in an initializer list. If you allocate two resources in an initializer list and the second one fails constructing then you have a leak (in the first resource) which you cannot possibly fix. – wilhelmtell Sep 4 at 2:40.
That's not possible. It's either you write a custom copy constructor (entirely on your own) or the compiler writes it for you. Note that if you write a copy constructor then you probably need a copy assignment and a destructor as well, because writing any of these three resource-management functions implies you're managing a resource.
With the copy-and-swap idiom, however, you only need to write the copy logic once, in the copy constructor, and you then define the assignment operator in terms of the copy constructor. Aside from that, I'm not entirely sure why you're using a shared_ptr. The point of a shared_ptr is to allow multiple pointers to safely point at the same object.
But you're not sharing the pointee, you deep-copy it. Maybe you should use a raw pointer instead, and free it in the destructor. Or, better yet, replace the shared_ptr with a clone_ptr, and then eliminate the copy constructor, copy assignment and destructor altogether.
1 Destructor and assignment should be fine here because the shared pointer basically already takes care of everything, but it's a very unusual design choice for sure. – Kerrek SB Sep 4 at 2:28 He probably needs at the very least an assignment operator, or else assigning to an object and copy-constructing an object would do different things -- which is counter-intuitive. But indeed, this is an unusual case, so much so that I just got confused myself into thinking he needs a destructor.
And that's a sign for a design that could do better (in the name of "least surprise"). – wilhelmtell Sep 4 at 2:36 What exactly is unusual here? The assignment operator is also defined in the "...".
That's not the point of the question. The point is that it would be nice to not have to re-write copying code for int a, b, c, d, e; that could already be generated by the compiler. – Lex Fridman Sep 4 at 2:46 @LexFridman it would be nice, yes, but again: that's not possible.
You're asking to write some of the copy logic, and delegate the rest for the compiler to write. C++ doesn't allow that: you either don't say a word, and the compiler generates the copy constructor for you, or you explicitly ask the compiler not to generate a copy ctor, and the compiler will write none of it for you. – wilhelmtell Sep 4 at 4:19 @LexFridman what's unusual is that your code as it is doesn't really manage a resource, and yet it relies on a custom copy logic.
That is unusual, and you could probably improve this design. – wilhelmtell Sep 4 at 4:42.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.