0

I have an object, and it is stored as a unique_ptr of the base class in a map.

class Base
{
    int foo;
};

map <string, unique_ptr<Base> > Objects;

however, the objects are actually of different child classes.

class Child1 : public Base
{
    int bar1;   
};
class Child2 : public Base;
{
    int bar2;
};

Objects.insert(make_pair("Child1", unique_ptr<Base>(new Child1())));
Objects.insert(make_pair("Child2", unique_ptr<Base>(new Child2())));

Now, somewhere far away in the code, I want to access a member variable of Child1, which is unique to that class.

cout << "Bar1 is:" << Objects["Child1"]->bar1 << endl;

But I get the error:

ERROR: 'class Base' has no member 'bar1'

So I try to use dynamic_cast, but of course, you cannot use dynamic_cast on a unique_ptr.

So what can I do?

Yaxlat
  • 665
  • 1
  • 10
  • 25
  • 1
    You are defeating the purpose of Polymorphism when you do not want to treat all your base class pointers the same. Use a Boost.Variant or another typesafe union to at least do it in a less hacky way than if you did it yourself. – AndyG Feb 13 '15 at 17:41
  • 1
    The member is `private` so how do you plan to access it anyway? – 5gon12eder Feb 13 '15 at 17:42
  • Since you are not using pointers to the base you can only access the base members. This is called object slicing. http://stackoverflow.com/questions/274626/what-is-object-slicing – NathanOliver Feb 13 '15 at 17:42
  • 1
    The `dynamic_cast` approach only works if there exists any virtual functions, which this code does not have. Give Base a `virtual ~Base()` and the compiler will then provide the Child with it's own virtual destructor, and dynamic-cast will work. – AndyG Feb 13 '15 at 17:42
  • @AndyG what do you mean? I am quite new to c++, and have no idea what you're talking about. I need to store several objects of different types which share a base class in an array, so I need to use polymorphism, right? – Yaxlat Feb 13 '15 at 17:44
  • @Yaxlat You should make the destructor of `Base` `virtual`. Then you can use `dynamic_cast` to downcast safely and the default deleter of `std::unique_ptr` will work correctly. – 5gon12eder Feb 13 '15 at 17:45
  • 1
    @Yaxlat: The nice thing about storing base class pointers is that you are treating all objects, base and derived, as the same. It's nice and convenient to have them all in one list. But the second that you suddenly need to care about whether the pointer is actually pointing to a derived type A or B means that you are not treating them all the same, so you haven't optimized. From experience, if you're developing a big system then you're going to have a huge headache later. Just store derived objects in their own arrays and you'll be happy later that it's so easy. – AndyG Feb 13 '15 at 17:46
  • Of course I'm going to have to treat them differently at one point, but that's not after I've made a lot of use of them being in the same array first. – Yaxlat Feb 13 '15 at 18:14
  • Why "Of course"? Derived classes will _behave_ differently but there is no reason why you must _treat_ them differently. – Chris Drew Feb 13 '15 at 18:35
  • What if I need the member of a child class? – Yaxlat Feb 13 '15 at 18:36
  • Via a virtual member function on the base class, for example. – Chris Drew Feb 13 '15 at 18:58
  • Yes but when I have 10 different child classes, each with different types of member variables, the base class becomes awfully messy – Yaxlat Feb 13 '15 at 19:00

1 Answers1

3
  1. Get a pointer to the base class from the unique_ptr.

    Base* basePtr = Objects["Child1"].get();
    
  2. Do a dynamic_cast on the pointer.

    Child1* child1Ptr = dynamic_cast<Child1*>(basePtr);
    
  3. Access the members of the Child1 class.

    if ( child1Ptr != nullptr )
    {
       // ... Use child1Ptr
    }
    

Make sure that you make the destructor of the base class virtual. Otherwise, the destructors of the derived classes will never be called when the unique_ptrs are destructed. Also, as observed by @AndyG in comments, dynamic_cast won't work without a any virtual functions.

class Base
{
    public:
        virtual ~Base(){}

    private:
       int foo;
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • The destructor is virtual! Thanks a lot for your help, it works! I was thinking of doing something along these lines, but didnt know how to convert from unique_ptr to a raw pointer, which is step 1. – Yaxlat Feb 13 '15 at 18:02