Re: inheritance, list of objects, polymorphism
* Vladimir Jovic:
Alf P. Steinbach wrote:
* Vladimir Jovic:
James Kanze wrote:
> General rule: assignment and external copy don't work well with
inheritance. (In my own code, I've gradually been introducing a
PolymorphicObject base class, with a virtual destructor and a private
copy constructor and assignment operator. With the rule that classes
designed to be used polymorphically should inherit from
PolymorphicObject.)
I do not understand why you said that "assignment and external copy
don't work well with inheritance."
Mainly it has to do with C++ variables directly being of the size of
the statically known type and directly containing an object of that
type, instead of just being pointers[1] as in Java and C# and like
languages.
When sizeof(Derived) > sizeof(Base) this means that
Base o = Derived();
performs a /slice/ of the Derived object; 'o' contains only the Base
stuff of that original object.
Ok, this explains it :)
[snip]
For a PolymorphicObject base class like James mentioned you therefore
generally want to introduce two restrictions, and support one main
functionality:
* Inhibit client code "slice" copying.
This is done by making the assignment operator private and the
copy constructor protected. James wrote "private" copy constructor
but that's a bit impractical. For you want to allow derived classes
to be clonable, and cloning is best expressed in terms of internal
copy construction.
The assignment operator is private and not implemented, or just private?
There's usually no need to implement it.
I can't imagine why one need it, but it's always like, someone's going to need
that anyway.
So the answer is "it depends", but in general, no implementation.
* Make sure that objects can only be created dynamically.
The reasonable way is to make the destructor protected.
But if you make the constructor protected, and leave the destructor
public, then you do not need next point. Or, am I missing something?
Yeah. First of all you need the destructor protected anyway, to stop the
long-fingered client code programmer from doing 'delete &*smart_ptr'. Or more
indirectly. And secondly, with M classes with on average N constructors each
then for protected constructors you need M*N factory functions, while with
destructor protected you need, uh, none. Or just a single common one, if
wrapping of new expression is regarded as a factory function. :-)
* Force use of smart pointer.
James relies on garbage collection so he probably doesn't do this,
but there are two aspects: ensuring that any newly created object's
raw pointer is immediately stored in a smart pointer, and ensuring
that only the smart pointer class has access to destroy an object.
One way to do the first it is to overload the class' allocation
function
(operator new) so that any direct 'new' expression would be overly
complicated. For C++98 then provide a macro that supplies the
requisite magic incomprehensible expression and ensures the pointer
is immediately wrapped in a smart pointer, before client code can
get at it. For C++0x I think the improved support for argument
forwarding makes the macro unnecessary. Anyways, one way to do the
second is to make destructor protected (which you'd do anyway for
the bullet point above), and grant friendship to the smart pointer.
Cheers & hth.,
- Alf