Copy constructor and =

Discussion in 'C++' started by Gandalf, Aug 15, 2003.

  1. Gandalf

    Gandalf Guest

    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?
     
    Gandalf, Aug 15, 2003
    #1
    1. Advertising

  2. "Gandalf" <> wrote in message
    news:wd3%a.20013$...
    > 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
     
    John Harrison, Aug 15, 2003
    #2
    1. Advertising

  3. Gandalf

    John Dibling Guest

    "Gandalf" <> wrote in message
    news:wd3%a.20013$...
    > 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."

    --
    - John Dibling
     
    John Dibling, Aug 15, 2003
    #3
  4. Gandalf

    Ron Natalie Guest

    "John Dibling" <> wrote in message news:T66%a.7829$...
    > "Gandalf" <> wrote in message
    > news:wd3%a.20013$...
    > > 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."


    The above "law" should not contain the word "virtual" prior to destructor.
     
    Ron Natalie, Aug 15, 2003
    #4
  5. John Harrison wrote:

    > 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);
    }
     
    E. Robert Tisdale, Aug 15, 2003
    #5
  6. Gandalf

    Heinz Ozwirk Guest

    "E. Robert Tisdale" <> schrieb im Newsbeitrag news:...
    : 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
     
    Heinz Ozwirk, Aug 15, 2003
    #6
  7. Gandalf

    Rolf Magnus Guest

    E. Robert Tisdale wrote:

    > John Harrison wrote:
    >
    >> 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;
    }
     
    Rolf Magnus, Aug 15, 2003
    #7
  8. Gandalf

    Gandalf Guest


    >
    > "Gandalf" <> wrote in message
    > news:wd3%a.20013$...
    >> 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

    Is there an example when things go wrong, due to not having a operator=
    defined for the class?
     
    Gandalf, Aug 16, 2003
    #8
  9. "Gandalf" <> wrote in message
    news:E%j%a.24337$...
    >
    >
    > >
    > > "Gandalf" <> wrote in message
    > > news:wd3%a.20013$...
    > >> 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

    > 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
     
    John Harrison, Aug 16, 2003
    #9
  10. "Gandalf" <> wrote in message
    news:Wdo%a.20139$...
    > > 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.

    > 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
     
    John Harrison, Aug 16, 2003
    #10
  11. "E. Robert Tisdale" <> wrote in message
    news:...
    > John Harrison wrote:
    >
    > > E. Robert Tisdale wrote:
    > >
    > >>John Harrison wrote:
    > >>
    > >>
    > >>>Bit of a problem if rhs.N != N.
    > >>
    > >>Of course.
    > >>But any discussion of exception handling here
    > >>is a digression which could only obfuscate the point.

    > >
    > > I'm afraid I'm obfuscated already.
    > > What point were you making with your bugged code?

    >
    > 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
     
    John Harrison, Aug 16, 2003
    #11
  12. "E. Robert Tisdale" <> wrote in message
    news:...
    > John Harrison wrote:
    >
    > > E. Robert Tisdale wrote:
    > >
    > >>John Harrison wrote:
    > >>
    > >>
    > >>>Bit of a problem if rhs.N != N.
    > >>
    > >>Of course.
    > >>But any discussion of exception handling here
    > >>is a digression which could only obfuscate the point.

    > >
    > > I'm afraid I'm obfuscated already.
    > > What point were you making with your bugged code?

    >
    > 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
     
    John Harrison, Aug 16, 2003
    #12
  13. John Harrison wrote:

    > E. Robert Tisdale wrote:
    >
    >>John Harrison wrote:
    >>
    >>>E. Robert Tisdale wrote:
    >>>
    >>>>John Harrison wrote:
    >>>>
    >>>>>Bit of a problem if rhs.N != N.
    >>>>
    >>>>Of course.
    >>>>But any discussion of exception handling here
    >>>>is a digression which could only obfuscate the point.
    >>>
    >>>I'm afraid I'm obfuscated already.
    >>>What point were you making with your bugged code?

    >>
    >>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.


    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.
     
    E. Robert Tisdale, Aug 16, 2003
    #13
  14. > >
    > > 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.
    >


    Why?

    john
     
    John Harrison, Aug 17, 2003
    #14
  15. Gandalf

    Ron Natalie Guest

    "E. Robert Tisdale" <> wrote in message news:...

    >
    > 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?
     
    Ron Natalie, Aug 18, 2003
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Aire
    Replies:
    3
    Views:
    471
    Mike Wahler
    Jan 25, 2004
  2. Replies:
    26
    Views:
    2,128
    Roland Pibinger
    Sep 1, 2006
  3. ali
    Replies:
    4
    Views:
    582
    David Harmon
    Mar 5, 2007
  4. Generic Usenet Account
    Replies:
    10
    Views:
    2,253
  5. cinsk
    Replies:
    35
    Views:
    2,619
    James Kanze
    Oct 11, 2010
Loading...

Share This Page