Re: Virtual constructor?
Azumanga wrote:
Imagine I have the following code:
struct A { .. };
struct B : public A { .. };
struct C : public A { .. };
// Copy the given object, return the copy
A* copy_object(A* obj)
{ ?? }
Does anyone have any good ideas on how to implement this function? At
the moment, I have two methods I can think of (sorry if these don't
actually compile, I have not got a compiler to hand here. The basic
ideas are I believe good however).
A* copy_object(A* obj)
{
if(B* b_obj = dynamic_cast<B*>(obj))
{ return new B(*b_obj); }
...
}
or:
struct A
{ virtual A* clone() { return new A(*this);} ... };
struct B : public A
{ virtual B* clone() { return new B(*this);} ... };
...
Both of these seem error prone, and require adding a new function for
everything in my heirachy. Is there a better way?
The following is a simple implementation of a smart pointer with copy
semantics. I post it only to illustrate the principle. A google search for
copy_ptr or clone_ptr should take you to more refined versions. If I recall
correctly, Axter has some on www.axter.com:
// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// ==================================
/*
| - Upon construction, copy_ptr<T> takes pointee ownership of
| a D* where D is a type derived from T. (compile type check)
| - In any copy construction or assignment a copy of the
| pointee is created. There should be no slicing.
| - Upon destruction the pointee is destroyed.
| - Intended use is within STL containers.
| - If T does not have a virtual destrucctor, copy_ptr<T>
| cannot be used polymorphically (compile time check).
*/
// we swap:
#include <algorithm>
// The clone_traits function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( static_cast<D*>( ptr ) ) ) );
}
template < typename T >
T * simple_clone ( T * ptr ) {
return( new T ( *ptr ) );
}
// forward declarations:
template < typename T >
class copy_ptr;
template < typename T >
void swap ( copy_ptr< T > &, copy_ptr< T > & );
// implementation:
template < typename T >
class copy_ptr {
friend void swap<> ( copy_ptr<T> &, copy_ptr<T> & );
/*
The idea is that in addition to a pointer, we also need
a pointer to the appropriate clone_traits function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );
public:
copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( simple_clone<T> )
{}
copy_ptr ( T * ptr )
: raw_ptr ( ptr )
, clone_fct ( simple_clone<T> )
{}
template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}
// copy construction clone_traitss:
copy_ptr ( copy_ptr const & other )
: raw_ptr ( other.clone_fct( other.raw_ptr ) )
, clone_fct ( other.clone_fct )
{}
// destruction frees the pointee
~copy_ptr ( void ) {
delete( raw_ptr );
}
// assignment reduces to copy construction:
copy_ptr & operator= ( copy_ptr const & other ) {
copy_ptr dummy ( other );
swap( *this, dummy );
return( *this );
}
T const * operator-> ( void ) const {
return( raw_ptr );
}
T * operator-> ( void ) {
return( raw_ptr );
}
T const & operator* ( void ) const {
return( *raw_ptr );
}
T & operator* ( void ) {
return( *raw_ptr );
}
}; // copy_ptr<T>
template < typename T >
void swap ( copy_ptr< T > & p, copy_ptr< T > & q ) {
std::swap( p.raw_ptr, q.raw_ptr );
std::swap( p.clone_fct, q.clone_fct );
}
// a sanity check:
#include <iostream>
struct Base {
Base ( void ) {
std::cout << "base is born.\n";
}
Base ( Base const & other ) {
std::cout << "base is copied.\n";
}
virtual ~Base () {
std::cout << "base dies.\n";
}
virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "base" );
}
};
std::ostream & operator<< ( std::ostream & ostr,
Base const & obj ) {
return( obj.dump( ostr ) );
}
struct Derived : public Base {
Derived ( void ) {
std::cout << "derived is born.\n";
}
Derived ( Derived const & other )
: Base ( other )
{
std::cout << "derived is copied.\n";
}
virtual ~Derived () {
std::cout << "derived dies.\n";
}
virtual
std::ostream & dump ( std::ostream & ostr ) const {
return( ostr << "derived" );
}
};
struct NotDerived {
NotDerived ( void ) {
std::cout << "not-derived is born.\n";
}
NotDerived ( NotDerived const & other )
{
std::cout << "not-derived is copied.\n";
}
virtual ~NotDerived () {
std::cout << "not-derived dies.\n";
}
};
struct BadBase {};
struct BadDerived : public BadBase {};
std::ostream & operator<< ( std::ostream & ostr, NotDerived const & obj ) {
return( ostr << "not-derived" );
}
int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );
std::cout << '\n' << *a_ptr << " " << *b_ptr << "\n\n";
a_ptr = b_ptr;
std::cout << '\n' << *a_ptr << " " << *b_ptr << "\n\n";
copy_ptr< int > i_ptr;
// compile time errors:
// copy_ptr< Base > x_ptr ( new NotDerived() );
// copy_ptr< int > j_ptr ( new Base() );
// copy_ptr< BadBase > bad_ptr ( new BadDerived () );
}
Best
Kai-Uwe Bux
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]