Copy constructor and =

G

Gandalf

Hello.
If I have classes that allocates dynamic memory, it is a good thing to have
a copy constructor(CC). If I have a CC, now I'm only trying to understand
when I also need to overload the assignment operator. When do I need that?
 
J

John Harrison

Gandalf said:
Hello.
If I have classes that allocates dynamic memory, it is a good thing to have
a copy constructor(CC). If I have a CC, now I'm only trying to understand
when I also need to overload the assignment operator. When do I need that?

Always. Your class cannot be used safely if you don't. Writing operator= is
easy if you have written a copy ctor

X& X::eek:perator=(const X& rhs)
{
X tmp(rhs); // copy rhs to tmp
swap(tmp); // swap tmp and this
return *this; // destroy tmp
}

swap is another member function which swaps two objects. Its usually also
very easy to write, all you do is swap each member variable in your class.
E.g. assuming X has member variables a, b and c

#include <algorithm>

void X::swap(X& rhs)
{
std::swap(a, rhs.a);
std::swap(b, rhs.b);
std::swap(c, rhs.c);
}

Include <algorithm> to get the std::swap function.

john
 
J

John Dibling

Gandalf said:
Hello.
If I have classes that allocates dynamic memory, it is a good thing to have
a copy constructor(CC). If I have a CC, now I'm only trying to understand
when I also need to overload the assignment operator. When do I need that?


Remember the Law of the Big Three:

"If your class needs a copy ctor, an operator=, or a virtual dtor, it
probably needs all three."
 
R

Ron Natalie

John Dibling said:
Remember the Law of the Big Three:

"If your class needs a copy ctor, an operator=, or a virtual dtor, it
probably needs all three."

The above "law" should not contain the word "virtual" prior to destructor.
 
E

E. Robert Tisdale

John said:
Writing operator= is easy
if you have written a copy ctor

X& X::eek:perator=(const X& rhs) {
X tmp(rhs); // copy rhs to tmp
swap(tmp); // swap tmp and this
return *this; // destroy tmp
}

swap is another member function which swaps two objects.
It's usually also very easy to write.
All you do is swap each member variable in your class.
E.g. assuming X has member variables a, b and c

#include <algorithm>

void X::swap(X& rhs) {
std::swap(a, rhs.a);
std::swap(b, rhs.b);
std::swap(c, rhs.c);
}

Include <algorithm> to get the std::swap function.

class X {
private:
size_t N;
double* A;
public:
size_t size(void) const { return N; }
const
double& operator[](size_t j) const {
return A[j]; }
X& operator=(const X&);
X(const X&);
};

Define operator= first:

X& X::eek:perator=(const X& rhs) {
for (size_t j = 0; j < N; ++j)
A[j] = rhs[j];
return *this;
}

then the copy constructor:

X::X(const X& rhs): N(rhs.size()), A(new double[N]) {
operator=(rhs);
}
 
H

Heinz Ozwirk

: class X {
: private:
: size_t N;
: double* A;
: public:
: size_t size(void) const { return N; }
: const
: double& operator[](size_t j) const {
: return A[j]; }
: X& operator=(const X&);
: X(const X&);
: };
:
: Define operator= first:
:
: X& X::eek:perator=(const X& rhs) {
: for (size_t j = 0; j < N; ++j)
: A[j] = rhs[j];
: return *this;
: }

This only works if N==rhs.N

: then the copy constructor:
:
: X::X(const X& rhs): N(rhs.size()), A(new double[N]) {
: operator=(rhs);
: }

In this case it might work, but remember that member variables are initialized/constructed in the order they appear in the class definition, which may be different from that of the initializers in the constructor's definition. It is also a waste of time to (default-) construct an object only to be able to use an assignment operator.

The best way to implement copy-c'tor and assignment is not implementing them at all. Using copy-constructable and assignable data members is less error prone then using raw pointers.

Heinz
 
R

Rolf Magnus

E. Robert Tisdale said:
John said:
Writing operator= is easy
if you have written a copy ctor

X& X::eek:perator=(const X& rhs) {
X tmp(rhs); // copy rhs to tmp
swap(tmp); // swap tmp and this
return *this; // destroy tmp
}

swap is another member function which swaps two objects.
It's usually also very easy to write.
All you do is swap each member variable in your class.
E.g. assuming X has member variables a, b and c

#include <algorithm>

void X::swap(X& rhs) {
std::swap(a, rhs.a);
std::swap(b, rhs.b);
std::swap(c, rhs.c);
}

Include <algorithm> to get the std::swap function.

class X {
private:
size_t N;
double* A;
public:
size_t size(void) const { return N; }
const
double& operator[](size_t j) const {
return A[j]; }
X& operator=(const X&);
X(const X&);
};

Define operator= first:

X& X::eek:perator=(const X& rhs) {
for (size_t j = 0; j < N; ++j)
A[j] = rhs[j];
return *this;
}

then the copy constructor:

X::X(const X& rhs): N(rhs.size()), A(new double[N]) {
operator=(rhs);
}

Sometimes, one can also see the opposite, using the copy constructor
(through placement new) to implement operator=:

#include <new>


X& X::eek:perator=(const X& rhs)
{
this->~X();
new(this) X(rhs);
return *this;
}
 
G

Gandalf

Always. Your class cannot be used safely if you don't. Writing operator=
is easy if you have written a copy ctor
Is there an example when things go wrong, due to not having a operator=
defined for the class?
 
J

John Harrison

Gandalf said:
Is there an example when things go wrong, due to not having a operator=
defined for the class?

Yes anytime you use operator=

class X
{
public:
X() : ptr(new int) { *ptr = 0; }
X(const& X rhs) : ptr(new int) { *ptr = *rhs.ptr; }
~X() { delete ptr; }
private:
int* ptr;
};

int main()
{
X a;
X b;
a = b;
}

After a = b, a.ptr will equal b.ptr. Then the destructors for a and b are
called, so a.ptr and b.ptr will be deleted but because they are the same
pointer, the same pointer will be deleted twice. Most likely your program
will crash.

Perhaps you think that a = b is not legal because you don't have operator=
for your class? That is not true, because if you don't define an operator=
the compiler will define one for you, same as for the copy constructor. But
when you have pointers and dynamic memory in a class the compiler defined
operator= is usually inadequate, that is why you should define your own.

john
 
J

John Harrison

Gandalf said:
But now I'm confused, Why is a and b pointing to the same thing after the
assignment? I thought that the copyconstructor was called when there were
no operator=, and to my best knowledge, I thought that it could handle the
pointers correctly.
SO the compilers operator= is just a silly, "make exact copy, bit by bit".
In that case I get it. Thanks.

Not quite right. The compilers operator= is to make a memberwise copy, not a
bitwise copy. So if class X has members a, b, c, which have types A, B, C
then the compiler generated X::eek:perator= will call A::eek:perator=,
B::eek:perator= and C::eek:perator=. Of course for built in types (including
pointers) operator= is a bitwise copy.

The important point about this is that if all the members of your class have
a good operator= defined then you don't have to define one yourself. If the
rule was a bitwise copy then virtually every class would need to define
operator= even if all the members of that class had a perfectly good
operator=.

These memberwise copy rules apply equally to copy constructors.

john
 
J

John Harrison

E. Robert Tisdale said:
You are confused.
Clearly.

There was no bug in my code.

Your class appeared to have no constructor other than a copy constructor.

Your assignment operator appeared not to deal correctly with the situation
where the objects were of different sizes. Though in mitagation it could be
said that it was impossible to construct such objects given the code you
quoted.

To me that seems bugged, but please explain. Maybe you mised out some code
that you thought wasn't relevant, or maybe I just don't understand.
Unlike your example, my example addresses the case
which concerned Gandalf -- classes that allocate dynamic memory.

My code intended was not to show exception safety, it was intended to show
that writing an assignment operator was easy once a copy constructor had
been written, which seemed to be the OP's situation.

john
 
J

John Harrison

E. Robert Tisdale said:
You are confused.
There was no bug in my code.

I still don't understand the point of your code and you still haven't
explained. Bugged or not, what is the point of defining a copy constructor
in terms of an assignment operator? You seem to think it preferable (as far
as I can tell) but I think you should explain why.

john
 
E

E. Robert Tisdale

John said:
I still don't understand the point of your code and you still haven't
explained. Bugged or not, what is the point of defining a copy constructor
in terms of an assignment operator? You seem to think it preferable (as far
as I can tell) but I think you should explain why.

Unlike your example, my example addresses the case
which concerned Gandalf -- classes that allocate dynamic memory.

In this case, it is better to define operator= first
then use it to define the copy constructor.
 
J

John Harrison

Unlike your example, my example addresses the case
which concerned Gandalf -- classes that allocate dynamic memory.

In this case, it is better to define operator= first
then use it to define the copy constructor.

Why?

john
 
R

Ron Natalie

E. Robert Tisdale said:
X& X::eek:perator=(const X& rhs) {
for (size_t j = 0; j < N; ++j)
A[j] = rhs[j];
return *this;
}

What happens when the assigned to value has a smaller value for
N than the right hand side?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top