K
Kai-Uwe Bux
Hi folks,
in another thread there is some discussion about cloning smart pointers. So
I started thinking about it and stumbled upon a rather simple generic
implementation. However, it uses dynamic_cast<>, and I am not really
familiar with that cast. So, I am not sure whether the code is correct.
Please have a look and let me know how to improve this.
Sorry for the long post
Thanks
Kai-Uwe Bux
// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// ==================================
/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T.
| - 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.
*/
// we swap:
#include <algorithm>
// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D*>( 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 function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );
public:
copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}
template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}
// copy construction clones:
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, p.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:stream & dump ( std:stream & ostr ) const {
return( ostr << "base" );
}
};
std:stream & operator<< ( std:stream & 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:stream & dump ( std:stream & ostr ) const {
return( ostr << "derived" );
}
};
int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";
a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";
}
in another thread there is some discussion about cloning smart pointers. So
I started thinking about it and stumbled upon a rather simple generic
implementation. However, it uses dynamic_cast<>, and I am not really
familiar with that cast. So, I am not sure whether the code is correct.
Please have a look and let me know how to improve this.
Sorry for the long post
Thanks
Kai-Uwe Bux
// copy_ptr.cc (C) Kai-Uwe Bux [2005]
// ==================================
/*
| - Upon construction, copy_ptr<T> takes pointee ownership
| a D* where D is a type derived from T.
| - 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.
*/
// we swap:
#include <algorithm>
// The clone function:
template < typename T, typename D >
T * clone ( T * ptr ) {
return( new D ( *( dynamic_cast<D*>( 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 function.
*/
T * raw_ptr;
T * ( *clone_fct ) ( T * );
public:
copy_ptr ( void )
: raw_ptr ( new T )
, clone_fct( clone<T,T> )
{}
template < typename D >
copy_ptr ( D * ptr )
: raw_ptr ( ptr )
, clone_fct ( clone<T,D> )
{}
// copy construction clones:
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, p.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:stream & dump ( std:stream & ostr ) const {
return( ostr << "base" );
}
};
std:stream & operator<< ( std:stream & 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:stream & dump ( std:stream & ostr ) const {
return( ostr << "derived" );
}
};
int main ( void ) {
copy_ptr< Base > a_ptr;
copy_ptr< Base > b_ptr ( new Derived() );
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";
a_ptr = b_ptr;
std::cout << '\n' << *a_ptr;
std::cout << " " << *b_ptr << "\n\n";
}