Why not use an = Operator instead of a Copy Constructor?

This is a discussion on Why not use an = Operator instead of a Copy Constructor? within the C++ Programming forums, part of the General Programming Boards category; As far as I can tell, the assignment operator does everything the copy constructor does. Does the assignment operator take ...

  1. #1
    Registered User
    Join Date
    Aug 2005
    Posts
    197

    Why not use an = Operator instead of a Copy Constructor?

    As far as I can tell, the assignment operator does everything the copy constructor does. Does the assignment operator take longer to run? Does the assignment operator not do the job when an object is passed to a function?

  2. #2
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,762
    They are similar but they have very different jobs. The copy constructor makes new things like something else out of nothing. Assignment on one object takes another object: the left hand side becomes like the right hand side.

    Example:
    Code:
    #include <iostream>
    
    class Cat
    {
    public:
        int my_age, my_mice_caught;
        
        Cat( int the_age = 1, int these_mice = 0 ) :
        my_age( the_age ),
        my_mice_caught( these_mice )
       {
       }
    };
    
    int main()
    {
        Cat a;
        Cat b( 5, 12 );
        std::cout <<"Before swap:\n";
        std::cout <<"a > mice caught: " <<a.my_mice_caught <<" age: " <<a.my_age <<".\n";
        std::cout <<"b > mice caught: " <<b.my_mice_caught <<" age: " <<b.my_age <<".\n";
    
        Cat *copy = new Cat( b );
        b = a;
        a = *copy;
        delete copy;
        std::cout <<"After swap:\n";
        std::cout <<"a > mice caught: " <<a.my_mice_caught <<" age: " <<a.my_age <<".\n";
        std::cout <<"b > mice caught: " <<b.my_mice_caught <<" age: " <<b.my_age <<".\n";
    }
    I took some liberties with that code, but nothing that you still can't learn from. Hopefully the compiler generated copy constructor and assignment operator are enough to teach you the difference between the two operations.

    But you should learn how to write your own, and this board is the very place to do it. Quite recently we've had a lot of threads on this very subject, so read them. Basically what works well for most objects is writing the copy constructor in terms of assignment, and then letting the assignment operator do the real work of copying over data from one object to the other.
    Last edited by whiteflags; 10-12-2007 at 09:03 PM.

  3. #3
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,274
    Quote Originally Posted by citizen View Post
    Basically what works well for most objects is writing the copy constructor in terms of assignment, and then letting the assignment operator do the real work of copying over data from one object to the other.
    That leads to double-initialization of everything. For instance:

    Code:
    class A
    {
    public:
        B b;
    
        A(const A &other)
        {
           *this = other;
        }
    
        A &operator=(const A &other)
        {
            b = other.b;
        }
    };
    
    A a1;
    A a2(a1);
    When a2 is constructed, the default constructor of class B is called. Then this newly constructed object is immediately overwritten with the data from a1's B object (via B's assignment operator). In other words, B::B() gets called and then B:perator=(const B &) gets called.

    Basically, the work performed by B::B() is a total waste because it is just going to be overwritten with some other object anyway. And what if there IS no function B::B() ?

    In general copy constructors should only call other copy constructors. In other words:

    Code:
        A(const A &other)
            : b(other.b)
        {
        }
    The code duplication can be annoying, true. But that should serve as motivation for you to design your classes so that you don't have to write copy constructors and assignment operators in the first place.
    Last edited by brewbuck; 10-12-2007 at 09:35 PM.

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    22,306
    Basically what works well for most objects is writing the copy constructor in terms of assignment, and then letting the assignment operator do the real work of copying over data from one object to the other.
    I think that what works well is the opposite: writing the copy assignment operator in terms of the copy constructor. Of course, you need to have a suitable destructor and swap function.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,762
    Admittedly that approach confuses me to no end because I have no idea how swap() works in that context. I'm not adverse to learning new things. I admit I'm not the brightest of the bunch but I find my approach good basically because I understand it well, and it's easy (for me) to either write an operator= that never fails or throws exceptions in a decent manner, (when some type is not default constructable - shame on you!). Brewbuck's advice is good because it delegates work, but B better be copy constructable too!

  6. #6
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Code:
    class A
    {
    public:
        A( const A&  a )
        {
            ...
        }
    
        A& operator=( const A&  a )
        {
            A temp( a );
            swap( temp, *this );
            return *this;
        }
    };
    Unless you've profiled your code and found that this type of assignment is actually having a noticable affect on performance, I agree with LaserLight. This way, if you change code in the copy constructor you don't have to worry about changing the assignment operator too (i.e. one less way for bugs to get into your code).

  7. #7
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,762
    My confusion is genuine. How can you swap A without it's assignment operator? Implementing the operator in terms of itself confuses me.

  8. #8
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,308
    Quote Originally Posted by citizen View Post
    My confusion is genuine. How can you swap A without it's assignment operator? Implementing the operator in terms of itself confuses me.
    Well, the kind of places where you even need to write your own copy constructor or assignment operator tend to be things that internally contain a bunch of pointers to other objects. Now there a good chance that operator = allocates more memory and does a deep copy of these members. However all swap should do is swap the pointer values around between the two objects - far less work!
    As a matter of fact, even if some of those members aren't just raw pointers, they may be types which themselves implement a specialisation of swap, which means your own swap specialisation can simply call swap on those members. It all boils down to a very cheap way to completely swap two objects.
    Last edited by iMalc; 10-13-2007 at 03:21 AM.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  9. #9
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Quote Originally Posted by laserlight View Post
    I think that what works well is the opposite: writing the copy assignment operator in terms of the copy constructor. Of course, you need to have a suitable destructor and swap function.
    wouldn't that break any RAII used in that class? because I destruct and than construct again while I just should assign some values, so I release and reacquire resources, which could take a long time or maybe even break things.

  10. #10
    Registered User
    Join Date
    Aug 2005
    Posts
    197
    Quote Originally Posted by brewbuck View Post
    In general copy constructors should only call other copy constructors. In other words:

    Code:
        A(const A &other)
            : b(other.b)
        {
        }
    What does the colon mean? Does it have something to do with inheritance?

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Quote Originally Posted by pheres View Post
    wouldn't that break any RAII used in that class? because I destruct and than construct again while I just should assign some values, so I release and reacquire resources, which could take a long time or maybe even break things.
    Depends entirely on how you write it. Basically, if you write a swap(), you must swap() all contained RAII members, too. Of course, this requires that the contained type has a proper swap() too. E.g.:
    Code:
    class two_contained
    {
      std::list<int> inner_list;
      std::vector<int> inner_vector;
    
    public:
      two_contained(const two_contained &other)
        : inner_list(other.inner_list), inner_vector(other.inner_vector)
      {}
    
      two_contained &operator =(const two_contained &other)
      {
        two_contained(other).swap(*this);
      }
    };
    OK, so far, so good. Now, we need to implement swap(). We do this simply by calling swap() for every member.
    Code:
    void swap(two_contained &other)
    {
      using std::swap;
      swap(inner_list, other.inner_list);
      swap(inner_vector, other.inner_vector);
    };
    Note two important points:
    1) Calling swap() for lists and vectors is a constant-time no-throw operation. Thus, the completed swap() is a constant-time no-throw operation. Operator =, which consists of the copy constructor and swap(), thus takes O(copy constructor) + O(swap) + O(destructor), and the swap() is constant time and thus irrelevant. Operator = therefore is about as efficient as any hand-written implementation. (No matter what, = must destruct the old data and copy-construct the new or something equivalent.)
    2) Note the using declaration. It's necessary (well, not in this case, but in others, and it's a good habit). Calling the function as std::swap() prevents argument-dependent lookup, and thus may miss out-of-std overloads of swap() that are specialized. Example:
    Code:
    namespace mine
    {
      template <typename E>
      class my_fancy_container
      {
        // ...
      public:
        void swap(my_fancy_container &other) throw(); // Specialized, O(1) swap.
      };
    
      template <typename E>
      void swap(my_fancy_container<E> &lhs, my_fancy_container<E> &rhs)
      {
        lhs.swap(rhs);
      }
    }
    Now, this specialized swap() can be found through ADL if you do swap(mfc1, mfc2) from anywhere at all. But if you have any explicit namespace qualification, i.e. you call std::swap(mfc1, mfc2), this function is not an option.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Ah, thanks. I hadn't even hear of ADL until now. good to know about it.

    But thinking again about laserlights statement
    writing the copy assignment operator in terms of the copy constructor
    : Your example doesn't use the copy ctor in the assignment operator. Or did I misunderstand something?

  13. #13
    Registered User
    Join Date
    Aug 2005
    Posts
    197
    Quote Originally Posted by laserlight View Post
    I think that what works well is the opposite: writing the copy assignment operator in terms of the copy constructor. Of course, you need to have a suitable destructor and swap function.
    I tried this
    Code:
    class point{
      public:
        point(){;}
        ~point(){;}
        point(const point&);
        double x, y, z;
        point &operator =(const point &right_side);         
    };
    
    point &point::operator =(const point &rhs){
          this(&rhs);
          return *this;      
    }
    
    point::point (const point &pt) {
          *this = pt;    
    }
    but it doesn't seem to work. Although the following does
    Code:
    class point{
      public:
        point(){;}
        ~point(){;}
        point(const point&);
        double x, y, z;
        point &operator =(const point &right_side);         
    };
    
    point &point::operator =(const point &rhs){
          x = rhs.x;
          y = rhs.y;
          z = rhs.z;
          return *this;      
    }
    
    point::point (const point &pt) {
          *this = pt;    
    }

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    22,306
    What does the colon mean? Does it have something to do with inheritance?
    No, it is an initialisation list that initialises the member variables. In particular, the member variable b of the current object is initialised to the member variable b of the other object.

    Your example doesn't use the copy ctor in the assignment operator. Or did I misunderstand something?
    It does. How else would you explain two_contained(other)?

    but it doesn't seem to work.
    What does this(&rhs) mean? It looks like you are trying to do some kind of constructor chaining (which is not a feature of C++) after the current object has been constructed. Even if this syntax was allowed, there would be a type mismatch since &rhs returns a point*, but the copy constructor does not take a point*. Read CornedBee's post as it pretty much describes what I had in mind.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Registered User
    Join Date
    Nov 2006
    Posts
    519
    Quote Originally Posted by laserlight View Post
    It does. How else would you explain two_contained(other)?
    I see the copy-ctor. but it isn't used inside operator=, as the statement "used in terms of copy constructor" suggests to me.

    Code:
      two_contained &operator =(const two_contained &other)
      {
        two_contained(other).swap(*this);   // swap, but no call to copy ctor
      }
    I guess I misunderstand something

Page 1 of 4 1234 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Copy constructor
    By dude543 in forum C++ Programming
    Replies: 26
    Last Post: 01-26-2006, 05:35 PM
  2. illegal copy constructor required?
    By ichijoji in forum C++ Programming
    Replies: 1
    Last Post: 03-08-2005, 06:27 PM
  3. copy constructor
    By Eber Kain in forum C++ Programming
    Replies: 1
    Last Post: 09-30-2002, 06:03 PM
  4. copy constructor
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 09-26-2001, 06:17 PM
  5. Using strings with the copy constructor
    By Unregistered in forum C++ Programming
    Replies: 3
    Last Post: 08-29-2001, 04:04 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21