5

I am reading effective C++ in Item 5, it mentioned two cases in which I must define the copy assignment operator myself. The case is a class which contain const and reference members.

I am writing to ask what's the general rule or case in which I must define my own copy constructor and assignment operator?

I would also like to know when I must define my own constructor and destructor.

Thanks so much!

skydoor
  • 25,218
  • 52
  • 147
  • 201
  • As far as I understand (and act) - an additional good practice (not to withdraw the other answers here) will be to follow "The rule of three": https://stackoverflow.com/q/4172722/1971003 – Guy Avraham Aug 04 '17 at 09:53

2 Answers2

6

You must create your own copy constructor and assignment operator (and usually default constructor too) when:

  • You want your object to be copied or assigned, or put into a standard container such as vector
  • The default copy constructor and assignment operator will not do the Right Thing.

Consider the following code:

class A; // defined elsewhere
class B {
private:
    A *my_very_own_a;
};

If you let the automatic copy constructor copy a B, it will copy the A * pointer value across, so that the copy points to the same A instance as the original. But part of the design of this class is that each B has its own A, so the automatic copy constructor has broken that contract. So you need to write your own copy constructor which will create a new A for the new B to point to.

However, consider this case:

class A; // defined elsewhere
class B {
private:
    A *shared_reference_to_a;
};

Here each B contains a pointer to an A, but the class contract doesn't demand a unique A for each B. So the automatic copy constructor might well do the Right Thing here.

Note that both examples are the same code, but with different design intent.

An example of the first situation might be B == dialog box, A == button. If you create a copy of a dialog box, it probably needs a copy of all the contents too.

An example of the second might be B == dialog box, A == reference to window manager. If you copy a dialog box, the copy probably exists in the same window manager as the original.

Philip Potter
  • 8,975
  • 2
  • 37
  • 47
4

The default copy constructor often causes two objects to "point to" a common piece of memory. This is because all the default copy constructor does is memberwise assignment. So if you have a pointer member, this can get you into trouble

    CClass::CClass(const CClass& src)
    {
       // these two point to the same place in memory now
       // you really aught to allocate new memory
       m_ptr = src.m_ptr;

       // not a big deal for members that aren't "pointing" elsewhere
       // this copy works great
       m_int = src.m_int;
    }

You essentially don't want to end up in a situation where two instances are "pointing to" the same place in memory. This can happen when you have pointers as members which point to dynamically allocated memory. This can also happen with references, both referring to something outside the class itself. This can happen with file handles, where you may need to duplicate/reopen a new file.

In fact, the latter case is probably the easiest to conceptualize. Pretend your 2 C++ objects both had a log file. Without a correctly functioning copy constructor, they could both be logging to the same file. What happens if in the classes destructor, the file is closed out? Well then the 2nd C++ object will have it's log file closed out from underneath him. One solution would be to create a copy-constructor that creates a new file for the newly constructed instance. So when the 1st C++ object goes away, it doesn't take the 2nd C++ object's log file with it. They have 2 independent log files, they can't interfere with each other.

This example can be extended to dynamic memory. Without a copy constructor, you will just have 2 pointers pointing to the same address. If the destructor of one deletes that memory, the other might not realize that the memory it's pointed to has gone away. Equally important, you probably don't want one object writing into the other object's member :).

Doug T.
  • 64,223
  • 27
  • 138
  • 202