Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why disable CObject's copy constructor and assignment

The MFC's root object CObject's copy constructor and assignment are disabled by default.

  • In MSDN, there is a description

The standard C++ default class copy constructor does a member-by-member copy. The presence of the private CObject copy constructor guarantees a compiler error message if the copy constructor of your class is needed but not available. You must therefore provide a copy constructor if your class requires this capability.

  • In CObject's source code, there is a comment:

Disable the copy constructor and assignment by default so you will get compiler errors instead of unexpected behaviour if you pass objects by value or assign objects.

My question is, what is the problem with the default bit-by-bit copy constructor for this CObject class? In my opinion, it would be better to give us the default copy constructor, and we could provide one if necessary (deep copy)

like image 729
Baiyan Huang Avatar asked Jun 21 '10 13:06

Baiyan Huang


People also ask

Why do we delete the copy constructor?

Copy constructor (and assignment) should be defined when ever the implicitly generated one violates any class invariant. It should be defined as deleted when it cannot be written in a way that wouldn't have undesirable or surprising behaviour.

Why do we delete copy constructor in C++?

The copy constructor and copy-assignment operator are public but deleted. It is a compile-time error to define or call a deleted function. The intent is clear to anyone who understands =default and =delete . You don't have to understand the rules for automatic generation of special member functions.

Why copy constructor and assignment operator's working is different?

The Copy constructor and the assignment operators are used to initializing one object to another object. The main difference between them is that the copy constructor creates a separate memory block for the new object. But the assignment operator does not make new memory space.


1 Answers

The default copy constructor is member-by-member, not bitwise.

Most CObject-derived classes contain - and manage directly - some system resources, that have no reference counting or similar mechanism, so the choice was probably made with the default use case in mind.

e.g. CGDIObject is roughly:

class CGDIObject : public CObject
{
    HGDIOBJ m_handle;

    CGDIObject() : m_handle(0) {}
    // derived classes provide a create, attach etc.
   ~CGDIObject() { DeleteObject(m_handle); } 
}

The default copy constructor here would be dangerous (leading to double destruction), providing a "correct" copy constructor is surprisingly hard and expensive.

Another reason may be that most CObject-derived classes are intended to be mutated, and thus passed by reference. A missing copy constructor will catch unintended copies that mutate a copy rather than the object passed:

class CMyObject : public CObject
{
   public:
      AttachFoo(FooHandle foo) { ... }
      AddBar() { ... }
};

bool InitMySession(CMyObject & obj)
{
    obj.AttachFoo(CreateRawFoo());   
    obj.AddBar();
    obj.AddBar();
}

// ...
CMyObj mo;
InitMySession(mo);

Omitting the "&" gives you code that compiles well, but creates a temporary copy, modifies that, and then drops it, while mo remains unmodified.

Quite many API's follow that pattern, as MFC doesn't rely on exceptions for error handling (for historic reasons: not all targeted compilers did support them well, and MFC requires a lot of extra resource handling that becomes painful with exceptions).


I don't think these choices are good, e.g. derived classes should be allowed to use the default copy constructor if their members permit (and most members should permit).

The decision fits the "mindset" of MFC, though, and the requriements / restrictions of the time MFC was created.

like image 183
peterchen Avatar answered Sep 20 '22 19:09

peterchen