From: Sebastian Karlsson on
Hi, I need to create copy constructors for a lot of my types in order
to fix the common pointer problems of shallow copying. I've never
thought much about it before but I've checked out the most common
tutorials and they seem to suggest what I've used in the past, namely:

class X
{
public:
X( const X& a_Other ) { member = a_Other.member; }
Y member;
};

The problem here is that first Ys default constructor is invoked and
then assignment. Using the default generated one only Ys copy
constructor is invoked. Considering how the code looks this is of
course pretty obvious. So I tried to use an initialization list in the
copy constructor instead, which gave me the same behaviour as the
default copy constructor. I can't recall seeing this used elsewhere
though which kind of boggles me.

The real question though is: Is it possible to first create a shallow
copy using the default copy constructor and then afterwards clean up
the pointer problems etc. I would stay have to pay for some unecessary
pointer copying, but it would save me from maintaining huge
initilization lists for types which mostly have default copyable
members.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Ulrich Eckhardt on
Sebastian Karlsson wrote:
> Hi, I need to create copy constructors for a lot of my types in order
> to fix the common pointer problems of shallow copying.

Not only that, but the same problem also makes a user-defined assignment
operator mandatory. Do a websearch for the so-called "law of three".

> I've never thought much about it before but I've checked out the most
> common tutorials and they seem to suggest what I've used in the past,
> namely:
>
> class X
> {
> public:
> X( const X& a_Other ) { member = a_Other.member; }
> Y member;
> };

....which is what only a bad tutorial would suggest. Good C++ code would use
initialisation instead of assignment.

> The problem here is that first Ys default constructor is invoked and
> then assignment. Using the default generated one only Ys copy
> constructor is invoked. Considering how the code looks this is of
> course pretty obvious. So I tried to use an initialization list in the
> copy constructor instead, which gave me the same behaviour as the
> default copy constructor.

The default copy-constructor just does memberwise copy-construction, so it's
the same as using an initialiser list manually.

> I can't recall seeing this used elsewhere though which kind of boggles me.

You haven't seen an initialiser list anywhere? In that case I'd say that
your learning material isn't worth that name. Check out the book reviews at
ACCU's.

> The real question though is: Is it possible to first create a shallow
> copy using the default copy constructor and then afterwards clean up
> the pointer problems etc.

What you can do is use shallow-copy initialisation and then manually clone
pointees and assign to the members, but what's the point? Also, this is
tough to get correct in the presence of exceptions.

> I would stay have to pay for some unecessary pointer copying, but it
> would save me from maintaining huge initilization lists for types which
> mostly have default copyable members.

Sorry, but that approach won't get you far. Copying a pointer is _WAY_
cheaper than allocating a piece of memory, so why even worry about
pointers? Also, you are trying to micro-optimise things when you don't even
have them working correctly yet.

Lastly, some more things I'd like to mention:
1. If possible, avoid dynamic allocation. Just use plain containment
instead.
2. Instead of implementing copying and assignment, you can also simply
forbid their use by declaring the copy constructor and assignment operator
but making them private and not implementing them.
3. If you use e.g. std::string instead of maintaining a char* to hold some
text, you get automatic copying and assignment for free. Internally, they
use pointers of course, but that isn't visible and that isn't something you
have to worry about.

Uli


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Sebastian Karlsson on
> You haven't seen an initialiser list anywhere? In that case I'd say that
> your learning material isn't worth that name. Check out the book reviews at
> ACCU's.

I do know about initialization lists, just not with a copy
constructor.

> Sorry, but that approach won't get you far. Copying a pointer is _WAY_
> cheaper than allocating a piece of memory, so why even worry about
> pointers? Also, you are trying to micro-optimise things when you don't even
> have them working correctly yet.
>


I just saw there was a typo in there which might have changed the
message. I was trying to say that I don't mind the copying of the
pointer, because as you said, it's cheap. I just don't want to
maintain a very long initialization list if I don't have to.


I'd also like to say that I do have things working correctly, it's
just that I'm trying to see if there's a way to avoid a long
initialization list for all my members which aren't pointers, for
which I just want the compiler generated default copy constructor
behavior.

Sorry for the confusion.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Ulrich Eckhardt on
Sebastian Karlsson wrote:

>> You haven't seen an initialiser list anywhere? In that case I'd say that
>> your learning material isn't worth that name. Check out the book reviews
>> at ACCU's.
>
> I do know about initialization lists, just not with a copy
> constructor.
>
>> Sorry, but that approach won't get you far. Copying a pointer is _WAY_
>> cheaper than allocating a piece of memory, so why even worry about
>> pointers? Also, you are trying to micro-optimise things when you don't
>> even have them working correctly yet.
>>
>
>
> I just saw there was a typo in there which might have changed the
> message. I was trying to say that I don't mind the copying of the
> pointer, because as you said, it's cheap. I just don't want to
> maintain a very long initialization list if I don't have to.
>
>
> I'd also like to say that I do have things working correctly, it's
> just that I'm trying to see if there's a way to avoid a long
> initialization list for all my members which aren't pointers, for
> which I just want the compiler generated default copy constructor
> behavior.

Ah, I get you now. ;)

In order to get the compiler-generated cctor, you must not redefine it
yourself. However, there are a few tricks.

1. Move all members which you want the default to take effect to a
structure. This class:

class foo {
int data_1;
...
float data_n;
bar* ptr;
};

....becomes this:

class foo {
struct {
int data_1;
...
float data_n;
} data;
bar* ptr;
};

....and the cctor only looks like this then:

foo::foo( foo const& rhs):
data(rhs.data), ptr(0) {
/// deep copy rhs.ptr
}

2. A different way is to not use pointers but a wrapper:

struct ptr_wrapper {
// copying, assignment, destructor
private:
bar* ptr;
};

This then replaces the member 'ptr' from class 'foo' above, which can then
use the default copy-constructor.


Note that in general, I would advise use of #2, because it makes things more
self-contained. Also note that if this wrapper's behaviour is specific to
class foo, you can also nest the wrapper inside that class to make this
explicit.

However, #1 can come handy, too, because sometimes you have special
constructors in a class (like e.g. std::string's) but otherwise still want
easy implementations for copy-construction, assignment and swapping, where
it's a popular mistake to forget one or two members.

Uli


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]