Re: Non-default copy constructors & assignment operators are harmful?
Tim Conkling wrote:
I have a large-ish class for which I've written a copy constructor.
When I add a new member variable to my class, I have to remember to
ask myself whether it should be copied in the copy constructor -- the
compiler won't help me to remember to do this. This sort of "invisible
dependency" between two pieces of code (in this case, a class
declaration and a copy constructor definition) scares me.
(I know that default compiler-supplied copy constructors and
assignment operators can be harmful for other reasons, but it's nice
that you don't have to remember to update them when you change a class
declaration.)
Design patterns like RAII help with some invisible dependencies -- by
eliminating the need to remember to always explicitly free a resource
after it's been required. Is there a pattern in C++ that can help
eliminate the need to keep class declarations and copy constructors/
assignment operators in sync? Are there patterns in other languages
that help with this sort of issue?
I am assuming that you are referring to pointers and upon invocation of
the copy constructor whether to perform a deep copy on that pointer?
You could use one of the smart pointers in the BOOST library. I've not
look at them in great detail, but I know that they are quite robust.
You just have to read how they work to use them correctly.
Alternatively RYO template wrapper class for the pointer which will copy
on member-wise copy. I.e.
// NOTE: When CONST is defined as const, it means that the constness is
// passed on to the object. This is not what C++ does by default
// when using raw pointers. If you want constness to be local
// define CONST as nothing.
#define CONST const /* constness *is* passed on to pointed at object */
//#define CONST /* constness *not* passed on to pointed at object */
template<typename * T>
class copyObjectPtr
{
T* wrappedPtr;
public:
// Default constructor
copyObjectPtr()
: wrappedPtr(NULL)
{}
// Constructor
copyObjectPtr(T* ptr)
: wrappedPtr(ptr)
{}
// Copy constructor
copyObjectPtr(copyObjectPtr const & ptrToCopy)
: wrappedPtr(wrappedPtr == NULL ? NULL : new T(*wrappedPtr))
{}
T* operator=(T* rhs)
{
return wrappedPtr = rhs; // perhaps you want to copy here too?
}
// Autocast (there are certain instances that this may not be
// invoked automatically)
operator T*() { return wrappedPtr; }
operator T CONST *() const { return wrappedPtr; }
T* ptr() { return wrappedPtr; }
T CONST * ptr() const { return wrappedPtr; }
// Member of operator
T* operator->() { return wrappedPtr; }
T CONST * operator->() const { return wrappedPtr; }
// Reference operator
T** operator&() { return &wrappedPtr; }
T CONST * const * operator&() const { return &wrappedPtr; }
// Dereference operator
T& operator*() { return *wrappedPtr; }
T CONST & operator*() const { return *wrappedPtr; }
};
I've not tested this as I've just wrote it right now, but it should work
/almost/ like a regular pointer. I think that the only instance it will
not work /exactly/ like a regular pointer, is if you pass it to a
template function or *I think* template class function that takes a T*.
For that, you would have to use the ptr() member function. This has
to do with template deduction rules.
Adrian
--
_____________________________________________________________________
\/Adrian_Hawryluk BSc. - Specialties: UML, OOPD, Real-Time Systems\/
\ My newsgroup writings are licensed under the Creative Commons /
\ Attribution-Noncommercial-Share Alike 3.0 License /
\_____[http://creativecommons.org/licenses/by-nc-sa/3.0/]_____/
\/______[blog:__http://adrians-musings.blogspot.com/]______\/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]