class Base {
public:
int b;
virtual Base *clone() = 0;
};
class Derived : public Base {
public:
int d;
Base *clone() { return new Derived(*this); }
};
Am I right in thinking that I need to include a definition of the
"clone" function for every class that I want to be able to clone
objects of? And that this would be true even if I didn't make it a
pure virtual function? And that there is no easy way round this, short
of using macros to "tidy" the code up? But conversely, am I right in
thinking that the automatically-generated copy constructors will do
the actual donkey work of making a copy of all the members at all the
levels for me?
(I was also going to include a question about a potential bug in VC++,
in which it complained about not being able to instatiate an abstract
class - but it turned out that the problem was that one declaration
was declared "const" and the other wasn't, so it was my fault.)
Thanks.
Paul.
Yes. In production code I often see a macro that does that. Don't
forget to write it in your class. Something like
#define DEFINE_CLONABLE(class_name) class_name* clone() \
{ return new class_name(*this); }
...
class Derived ...
DEFINE_CLONABLE(Derived)
};
> And that this would be true even if I didn't make it a
> pure virtual function?
If you didn't make it pure virtual, what would its body look like?
> And that there is no easy way round this, short
> of using macros to "tidy" the code up?
Not really. Unless I'm missing something obvious, of course.
> But conversely, am I right in
> thinking that the automatically-generated copy constructors will do
> the actual donkey work of making a copy of all the members at all the
> levels for me?
Seems so.
> (I was also going to include a question about a potential bug in VC++,
> in which it complained about not being able to instatiate an abstract
> class - but it turned out that the problem was that one declaration
> was declared "const" and the other wasn't, so it was my fault.)
That would certainly do it. :-)
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
> I've got various classes derived (directly or indirectly) from a
> single base, and want a function to make a copy of a given object. My
> code is along the following lines:
>
> class Base {
> public:
> int b;
> virtual Base *clone() = 0;
> };
>
> class Derived : public Base {
> public:
> int d;
> Base *clone() { return new Derived(*this); }
> };
>
> Am I right in thinking that I need to include a definition of the
> "clone" function for every class that I want to be able to clone
> objects of? And that this would be true even if I didn't make it a
> pure virtual function? And that there is no easy way round this, short
> of using macros to "tidy" the code up?
You can use a template. Without having it run through a compiler, something like this could/should work:
class Base
{
public:
virtual Base* clone() = 0;
};
template <typename T>
class ClonableBase : public Base
{
public:
virtual Base* clone()
{
return new T(*this);
}
}
class Derived: public ClonableBase<Derived>
{
...
};
This should work since every derived class inherits its own implementation of clone() from a template-generated 'buffer' class. However, I do not see much to be gained by this. Just implement the virtual constructors in the derived classes, that way at least the semantics become immediately clear.
> But conversely, am I right in
> thinking that the automatically-generated copy constructors will do
> the actual donkey work of making a copy of all the members at all the
> levels for me?
This works if your class looks more or less like a traditional C struct (i.e. a POD type, or 'plain old data'). However, be very, very careful if the constructor performs resource aquisition (and the destructor therefore frees resources). Then it will most probably NOT work, and can even lead to double-deletes and such pains.
As a rule, if your class contains at least one of the following, it will most probably need all three: destructor, copy constructor, assignment operator. In general do no depend on the generated versions, except in very simple cases; they have a tendency to not do what you want or need.
> (I was also going to include a question about a potential bug in VC++,
> in which it complained about not being able to instatiate an abstract
> class - but it turned out that the problem was that one declaration
> was declared "const" and the other wasn't, so it was my fault.)
Yes. C++ currently lacks an 'override' keyword (C++ 0x will get it, though), so you cannot tell the compiler that a function is supposed to override a function in the base class. This means that the compiler cannot give a meaningful error message, since it would have to 'guess' what yo actually want to achieve. That said, the Microsoft compiler _does_ support an override modifier as an extension. However, using non-standard extensions lead to non-portable code.
Robert
(snip)
> > Am I right in thinking that I need to include a definition of the
> > "clone" function for every class that I want to be able to clone
> > objects of?
>
> Yes. In production code I often see a macro that does that. Don't
> forget to write it in your class. Something like
>
> #define DEFINE_CLONABLE(class_name) class_name* clone() \
> { return new class_name(*this); }
>
> ...
> class Derived ...
> DEFINE_CLONABLE(Derived)
> };
Interesting. I thought macros were generally disapproved of? I'll have
a think as to whether to do it this way.
> > And that this would be true even if I didn't make it a
>
> > pure virtual function?
>
> If you didn't make it pure virtual, what would its body look like?
At one stage the body (in the base class, which was abstract due to
another pure virtual function) was { return NULL; } which satisfied
the compiler. A horrible kludge, though, and I'm glad it's gone.
(more snip)
Thanks for the help!
Paul.
So far I haven't learnt anyhting about templates, but this is further
evidence that it would be a good idea if I did.
> This should work since every derived class inherits its own implementation of clone() from a template-generated 'buffer' class. However, I do not see much to be gained by this. Just implement the virtual constructors in the derived classes, that way at least the semantics become immediately clear.
>
> > But conversely, am I right in
> > thinking that the automatically-generated copy constructors will do
> > the actual donkey work of making a copy of all the members at all the
> > levels for me?
>
> This works if your class looks more or less like a traditional C struct (i.e. a POD type, or 'plain old data'). However, be very, very careful if the constructor performs resource aquisition (and the destructor therefore frees resources). Then it will most probably NOT work, and can even lead to double-deletes and such pains.
>
> As a rule, if your class contains at least one of the following, it will most probably need all three: destructor, copy constructor, assignment operator. In general do no depend on the generated versions, except in very simple cases; they have a tendency to not do what you want or need.
Ah. I knew about the "rule of three". But you saying it makes me
realise that my class does in fact have a memory leak, one which I'd
totally overlooked before.
> > (I was also going to include a question about a potential bug in VC++,
> > in which it complained about not being able to instatiate an abstract
> > class - but it turned out that the problem was that one declaration
> > was declared "const" and the other wasn't, so it was my fault.)
>
> Yes. C++ currently lacks an 'override' keyword (C++ 0x will get it, though), so you cannot tell the compiler that a function is supposed to override a function in the base class. This means that the compiler cannot give a meaningful error message, since it would have to 'guess' what yo actually want to achieve. That said, the Microsoft compiler _does_ support an override modifier as an extension. However, using non-standard extensions lead to non-portable code.
Many thanks for your help.
Paul.
> Interesting. I thought macros were generally disapproved of? I'll have
> a think as to whether to do it this way.
Disapproved in principle: no, not exactly. Disapproved when unnecessary: yes. Macros are dangerous, because they happen _before_ any 'real' language evaluation. The macro preprocessor is a glorified text replacement facility after all, nothing more. This does NOT mean that macros are without uses; in fact there are several scenarios where a macro is not only a possibility, but actually a superior one.
Problems arise when macros try to be "too clever". Since they are nigh unparsable by an IDE, they tend to lower readability and can (but not necessarily have to) make the code more difficult to maintain. As is the case with most things in C and C++: the art is to know when to use a language facility (and when not).
My personal rule is: avoid macros where sensible. Especially avoid meta-programming with macros, generating of classes and types, changing of identifier names and such. Many things can be achieved with templates, though not everything. And templates are a very complex additional meta-language, so they do not necessarily make things easier for you, especially in the beginning. They do respect types and namespaces, though, which is IMHO most important.
Robert
Templates are an integral part of modern C++, so you really should familiarise yourself with them. However, be aware that templates form a complete declarative meta-language. It is a lot like functional programming (just not on data but on types), and can get complicated quite quickly. Templates are difficult to read, and some of the compiler-errors produced by erroneous templates are more or less impossible to decipher. So start with simple cases. The most important thing to remember is that a C++ template is _not_ the same as generics in Java or C#. A template produces no code, unless it is 'instantiated' with the corresponding type or constant. Only than does the compiler produce code, not before.
The example I have shown is in fact not a 'simple' case, since the template receives the type of the inheriting class as its parameter (yes, that's legal). So each inheriting class is derived from its specific super class, which in turn inherits from the base class (and is generated automatically from the template). Stuff like that really can make the brain hurt if one is not used to it... at least it was like that for me.
Robert