I'm new to smart pointers and I would be really grateful if somebody could give me a hint whether the way I'm handling smart pointers as class members is correct. More precisely, the solution that I would like to achieve is in the context of class polymorphism and should be ideally exception-safe.
Given a container of heterogeneuous objects (std::vector<shared_ptr<CBase> > my_vector), the usual way to add elements is: my_vector.push_back( shared_ptr<CBase>(new CChild(1))), so that later on, one can call the member function of the specific derived class by doing: my_vector[0]->doSomething().
What I would like to achieve is to add stack objects to the vector and still being able to do polymorphism. Intuitively sth. like:
CChild<float> obj1(1); my_vector.push_back(obj1). To solve that, I'm using now the Virtual Constructor Idiom:CChild obj1(1); my_vector.push_back(obj1.clone());.
Note that insomeof my derived classes, I've static member functions that create objects, e.g:CChild<float> obj1 = CChild<float>::initType2(1);Because of requirement issues and also to have a clean interface, I've now a new class
CFoo<T>that has as data member a smart pointer to theCBase<T>class.
The idea is that besides containing other new private members, this class encapsulates/handles the smart pointers to the derived objects, such that I'm allowed to do sth. like:CFoo<float> myfoo(CChild<float>::initType2(1)); my_vector.push_back(myfoo);. This means that the container is now of typevector<CFoo<T> >instead of typevector<shared_ptr<CBase> >
It's in this context, that I would like to know how to implement the constructors for a class with smart pointers as class members? What about the implementation of the operator = following the copy-swap idiom? Below, I give some ilustrations of my class design:
template < typename T >
class CBase{
public:
CBase(){};
virtual ~CBase(){};
...
virtual CBase<T> * clone() const = 0;
virtual CBase<T> * create() const = 0;
};
template < typename T >
class CChild1 : public CBase{
public:
...
CChild1<T> * clone() const { return new CChild1<T>(*this); }
CChild1<T> * create() const { return new CChild1<T>(); }
static CChild1 initType1(double, double);
static CChild1 initType2(int);
};
template < typename T >
struct type{
typedef std::tr1::shared_ptr<T> shared_ptr;
};
template < typename T >
class CFoo{
public:
CFoo();
CFoo( const CBase<T> &, int = 0 );
CFoo( const CFoo<T> & );
void setBasePtr( const CBase<T> & );
void swap( CFoo<T> & );
CFoo<T> & operator = ( CFoo<T> );
...
~CFoo();
private:
typename type<CBase<T> >::shared_ptr m_ptrBase;
int m_nParam;
};
template < typename T >
CFoo<T>::CFoo()
:m_nParam(0)
// How shall I handle here the "m_ptrBase" class member? e.g point it to NULL?
{
}
template < typename T >
CFoo<T>::CFoo(const CBase<T> & refBase, int nParam)
:m_ptrBase(refBase.clone()), // Is this initialization exception-safe?
m_nParam(nParam)
{
}
template < typename T >
CFoo<T>::CFoo(const CFoo<T> & refFoo)
:m_ptrBase(refFoo.m_ptrBase),
m_nParam(refFoo.m_nParam)
{
}
template < typename T >
void CFoo<T>::setBasePtr( const CBase<T> & refBase ){
// ??? I would like to do sth. like: m_ptrBase(refBase.clone())
}
template < typename T >
CFoo<T>::~CFoo(){
// The memory is going to be freed by the smart pointer itself and therefore
// the destructor is empty, right?
}
template < typename T >
void CFoo<T>::swap( CFoo<T> & refFoo ){
//does this here makes sense?
using std::swap;
swap(m_ptrBase, refFoo.m_ptrBase);
swap(m_nParam, refFoo.m_nParam);
}
template < typename T >
CFoo<T> & CFoo<T>::operator = ( CFoo<T> copyFoo ){
copyFoo.swap(*this);
return (*this);
}
Below an example on what I would like to intuitively achieve. First, I fill the container with CFoo<float> objects that contain smart pointers to derived classes, besides another integer class member (Note that all this is only illustrative).
std::vector<CFoo<float> > my_bank;
for (int b=0; b < 3; b++){
float x = b*sqrt(2);
my_bank.push_back( new CFoo<float>( CChild1<float>::initType2(x), b) );
}
for (double s= 1.0; s<= 8.0; s *= 2.0){
my_bank.push_back( new CFoo<float>( CChild2<float>::initType2(x), 0) );
}
Once, the container is filled, I would like to do some operations, calling to virtual functions, e.g. doSomething that are specialized in each derived class.
for (int i=0; i < (int)my_bank.size(); i++){
int b = my_bank[i].m_nParam;
CBase<float>* myChild = my_bank[i].m_ptrBase;
myChild->doSomething( param1, param2, param3, ..., b);
}
vectoris an owning container, so you shouldn't have any automatic objects that you put in the container. Why not add the element to the container first and then use that one in place of your original automatic object? Otherwise you can always just insert a copy:v.push_back(shared_ptr<Base>(new Derived(obj)));- Kerrek SBadding the element to the container first? Do you mind in illustrating your suggestion? As for thecopy insertion, what does theDerived(obj)is exactly? How could I also add an object created using thestatic function member? - TinFoo x; /* do stuff with x */ v.push_back(new Foo(x));, you could say:v.push_back(new Foo); Foo & x = v.back(); /* now use x */For the second point, what part of it is unclear? I'm just making a straight copy. - Kerrek SBCChild<float> obj1 = CChild<float>::initType2(1). How could I follow this type of instantiation, to do intuitively something like:v.push_back(shared_ptr<Base>(new CChild<float>::initType2(1)))? - TininitType2return a shared pointer already rather than a naked pointer... but more fundamentally, I just think you might not need any of that wrapper class at all. I don't see how it's useful or which problem it solves that can't be solved much more simply. - Kerrek SB