10

I am facing a situation in which I have a std::vector of boost::shared_ptrs of a base class. During the course of my program I need to store shared pointers to derived class objects in that vector too and at some time later in the program, need to retrieve those shared pointers.

Following code illustrates my problem:

#include <iostream>
#include <vector>
using namespace std;

#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>

class Base
{
public:
    virtual ~Base()
    {
    }
};
/******************************************/

typedef boost::shared_ptr< Base > BasePtr;
/******************************************/

class Derived1 : public Base
{
public:
    void derived1_test()
    {
        cout << "derived1_test" << endl;
    }
    /******************************************/
    int i1;
};
/******************************************/

typedef boost::shared_ptr< Derived1 > Derived1Ptr;
/******************************************/

class Derived2 : public Base
{
public:
    void derived2_test()
    {
        cout << "derived2_test" << endl;
    }
    /******************************************/
    int i2;
};
/******************************************/

typedef boost::shared_ptr< Derived2 > Derived2Ptr;
/******************************************/

int main()
{
    Derived1Ptr d1 = boost::make_shared< Derived1 >();
    Derived2Ptr d2 = boost::make_shared< Derived2 >();

    vector< BasePtr > v;
    v.push_back( d1 );
    v.push_back( d2 );
    BOOST_FOREACH(BasePtr bPtr, v)
    {
        try
        {
            Derived1& d11 = dynamic_cast< Derived1& >( *bPtr );
            d11.derived1_test();
        }
        catch (const std::bad_cast& e)
        {
            Derived2& d22 = dynamic_cast< Derived2& >( *bPtr );
            d22.derived2_test();
        }
    }
    return 0;
}

In the above code, if I change the code in BOOST_FOREACH from

Derived1& d11 = dynamic_cast< Derived1& >( *bPtr );

to

Derived1Ptr d11 = dynamic_cast< Derived1Ptr >( bPtr );

, I get following compile time error on VS2010

invalid target type for dynamic_cast target type must be a pointer or reference to a defined class

My problem is that I want to work with boost::shared_ptr not references. Secondly, I am using dynamic_cast which will throw a std::bad_cast exception when object reference is of a different type (tried using it with shared pointers but get the compiler error mentioned previously). That is clearly very slow. I want to be able to use a more performance oriented approach. What I am looking for here is any solution instead of using dynamic_cast and exception handling.

Any suggestion with regards to code or change in design are welcome.

Aaron S
  • 5,023
  • 4
  • 29
  • 30
  • If there is only one derived class or you are certain of it's type, you can just use `static_cast` to downcast. – JohnPS Mar 03 '11 at 13:40
  • Actually this is a much simpler version. In the actual code there are 3 different derived classes. – Aaron S Mar 03 '11 at 13:45
  • And I realized that you are using `shared_ptr` not plain pointers, so disregard my advice. – JohnPS Mar 03 '11 at 13:47

4 Answers4

13

Use dynamic_pointer_cast

Henrik
  • 23,186
  • 6
  • 42
  • 92
7

Your design seems to be missing the point; it would be better to use virtual functions to achive polymorphic behaviour.

#include <iostream>
#include <vector>
using namespace std;

#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>

class Base
{
public:
    virtual ~Base() {}
    virtual void test() = 0;
};
/******************************************/

typedef boost::shared_ptr< Base > BasePtr;
/******************************************/

class Derived1 : public Base
{
public:
    void test()
    {
        cout << "derived1_test" << endl;
    }
    /******************************************/
    int i1;
};

class Derived2 : public Base
{
public:
    void test()
    {
        cout << "derived2_test" << endl;
    }
    /******************************************/
    int i2;
};

int main()
{
    BasePtr d1 = boost::make_shared< Derived1 >();
    BasePtr d2 = boost::make_shared< Derived2 >();

    vector< BasePtr > v;
    v.push_back( d1 );
    v.push_back( d2 );
    BOOST_FOREACH(BasePtr &bPtr, v) // I use a reference here for efficiency.
    {
        bPtr->test();
    }
    return 0;
}
Steve
  • 1,760
  • 10
  • 18
3

If your vectors own the objects, you would be better off using a boost::ptr_vector.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
1

You have several options:

Use inheritance (and polymorphism) properly! i.e. define pure virtual methods in your base class which you implement in your derived class (also, the extra shared_ptr type for each derived type is redundant!)

Use a variant type to hold all derived types (useful if you have a limited set). There is no need for dynamic cast then, you can specify a visitor to do what you need to.

Nim
  • 33,299
  • 2
  • 62
  • 101