The MFC's root object CObject's copy constructor and assignment are disabled by default.
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.
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)
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With