marius said:
One cannot define a polymorphic STL container of smart pointers. Smart
pointers do not carry the polymorphic traits of the types they wrap;
they become completely new types. They are useful though for
non-polymorphic (uniform) pointer containers.
class B : public A {};
smart_ptr<B> and smart_ptr<A> are *unrelated*.
That is not necessarily true. If B derives from A, then an assignment
A* a_ptr;
B* b_ptr;
a_ptr = b_ptr;
makes sense wheread
b_ptr = a_ptr;
requires a cast. There is not intrinsic reason, why smart pointers could not
implement this. Below, you find a simple proof of concept with a reference
counted shared pointer. The same thing can easily be done for a smart
pointer with deep copy semantics.
However, there is a good reason why one usually would not bother
implementing this in a smart pointer: the allocation idiom
smart_ptr<Base> t_ptr ( new Derived ( ... ) );
will usually provide enough polymorphism.
// Proof of concept
// ================
#include <algorithm>
#include <functional>
template < typename T >
class refcount_ptr;
template < typename T >
void swap ( refcount_ptr< T > &, refcount_ptr< T > & );
template < typename D, typename T >
refcount_ptr<D> up_cast_dynamic ( refcount_ptr<T> const & t_ptr );
template < typename D, typename T >
refcount_ptr<D> up_cast_static ( refcount_ptr<T> const & t_ptr );
template < typename T >
class refcount_ptr {
friend void swap<> ( refcount_ptr<T> &, refcount_ptr<T> & );
template < typename D, typename S >
friend
refcount_ptr<D> up_cast_dynamic ( refcount_ptr<S> const & );
template < typename D, typename S >
friend
refcount_ptr<D> up_cast_static ( refcount_ptr<S> const & );
template < typename S >
friend class refcount_ptr;
unsigned long* c_ptr;
T * t_ptr;
template < typename B >
refcount_ptr ( refcount_ptr<B> const & other, int )
: c_ptr ( other.c_ptr )
, t_ptr ( dynamic_cast< T* >( other.t_ptr ) )
{
++ (*c_ptr);
}
template < typename B >
refcount_ptr ( refcount_ptr<B> const & other, bool )
: c_ptr ( other.c_ptr )
, t_ptr ( static_cast< T* >( other.t_ptr ) )
{
++ (*c_ptr);
}
public:
refcount_ptr ( T * ptr = 0 )
: c_ptr( new unsigned long ( 1 ) )
, t_ptr( ptr )
{}
refcount_ptr ( refcount_ptr const & other )
: c_ptr ( other.c_ptr )
, t_ptr ( other.t_ptr )
{
++ (*c_ptr);
}
template < typename D >
refcount_ptr ( refcount_ptr<D> const & other )
: c_ptr ( other.c_ptr )
, t_ptr ( other.t_ptr )
{
++ (*c_ptr);
}
~refcount_ptr ( void ) {
-- (*c_ptr);
if ( (*c_ptr) == 0 ) {
delete( c_ptr );
delete( t_ptr );
}
}
refcount_ptr & operator= ( refcount_ptr const & other ) {
refcount_ptr tmp ( other );
swap( *this, tmp );
return( *this );
}
template < typename D >
refcount_ptr & operator= ( refcount_ptr<D> const & other ) {
refcount_ptr tmp ( other );
swap( *this, tmp );
return( *this );
}
T const * operator-> ( void ) const {
return( t_ptr );
}
T * operator-> ( void ) {
return( t_ptr );
}
T const & operator* ( void ) const {
return( *( this->operator->() ) );
}
T & operator* ( void ) {
return( *( this->operator->() ) );
}
bool operator== ( refcount_ptr const & other ) const {
return ( this->t_ptr == other.t_ptr );
}
bool operator!= ( refcount_ptr const & other ) const {
return ( this->t_ptr != other.t_ptr );
}
bool operator< ( refcount_ptr const & other ) const {
return ( std::less<T*>( this->t_ptr, other.t_ptr ) );
}
bool operator<= ( refcount_ptr const & other ) const {
return ( std::less_equal<T*>( this->t_ptr, other.t_ptr ) );
}
bool operator> ( refcount_ptr const & other ) const {
return ( std::greater<T*>( this->t_ptr, other.t_ptr ) );
}
bool operator>= ( refcount_ptr const & other ) const {
return ( std::greater_equal<T*>( this->t_ptr, other.t_ptr ) );
}
};
template < typename T >
void swap ( refcount_ptr< T > & p, refcount_ptr< T > & q ) {
std::swap( p.c_ptr, q.c_ptr );
std::swap( p.t_ptr, q.t_ptr );
}
template < typename D, typename T >
refcount_ptr<D> up_cast_dynamic ( refcount_ptr<T> const & p ) {
return ( refcount_ptr<D>( p, 1 ) );
}
template < typename D, typename T >
refcount_ptr<D> up_cast_static ( refcount_ptr<T> const & p ) {
return ( refcount_ptr<D>( p, true ) );
}
#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\n" );
}
};
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\n" );
}
};
struct Unrelated {
Unrelated ( void ) {
std::cout << "derived is born.\n";
}
Unrelated ( Unrelated const & other )
{
std::cout << "derived is copied.\n";
}
virtual ~Unrelated () {
std::cout << "derived dies.\n";
}
virtual
std:
stream & dump ( std:
stream & ostr ) const {
return( ostr << "unrelated\n" );
}
};
int main ( void ) {
refcount_ptr< Base > a_ptr ( new Base() );
refcount_ptr< Base > b_ptr ( new Derived() );
refcount_ptr< Derived > d_ptr ( new Derived() );
refcount_ptr< Base > c_ptr ( d_ptr );
refcount_ptr< Derived > e_ptr;
d_ptr->dump( std::cout );
c_ptr->dump( std::cout );
a_ptr->dump ( std::cout );
a_ptr = d_ptr;
a_ptr->dump( std::cout );
b_ptr->dump( std::cout );
// uncommenting the following yields a compile time error:
/*
refcount_ptr< Unrelated > u_ptr;
b_ptr = u_ptr;
*/
e_ptr = up_cast_dynamic< Derived >( a_ptr );
e_ptr->dump( std::cout );
a_ptr = refcount_ptr<Base>();
std::cout << "first handle deleted.\n";
b_ptr = refcount_ptr<Base>();
std::cout << "second handle deleted.\n";
c_ptr = refcount_ptr<Base>();
std::cout << "third handle deleted.\n";
d_ptr = refcount_ptr<Derived>();
e_ptr = refcount_ptr<Derived>();
}
Sorry for the long post.
Kai-Uwe Bux