Can't avoid object slicing in c++

I want to construct a std::vector containing objects derived from the Base class. I know that I can't directly push the whole object into the vector (cause of object slicing) so I use smart pointers. However, it still doesn't work. What am I doing wrong?

My code:


struct Base{
    int num1;
    explicit Base(int num1){
        this->num1 = num1;
    }
};

struct Derived : Base{
    int num2;
    explicit Derived(int num1, int num2) : Base(num1){
        this->num2 = num2;
    }
};


int main()
{
    std::vector<std::unique_ptr<Base>> someList;
    someList.push_back(std::make_unique<Derived>(100, 1));
    someList.push_back(std::make_unique<Derived>(100, 2));
    std::cout << someList[0]->num2 << std::endl; // <---- Can't access num2 !
}

Upvotes: 0

Views: 454

Answers (1)

aschepler
aschepler

Reputation: 72271

The Derived objects and their num2 members are there, but the type system doesn't know that (and in similar code, it might not be certain).

The type of someList[0] is std::unique_ptr<Base>, so the -> operator allows naming members of Base. In general, a unique_ptr<Base> might not point at a Derived at all, so this is the safe way.

If the Base type were polymorphic, you could use dynamic_cast to check if the object really is a Derived. To get this working, let's add a virtual destructor to Base:

struct Base{
    int num1;
    explicit Base(int num1){
        this->num1 = num1;
    }
    virtual ~Base() = default;
};

Then we can do:

int main()
{
    std::vector<std::unique_ptr<Base>> someList;
    someList.push_back(std::make_unique<Derived>(100, 1));
    someList.push_back(std::make_unique<Derived>(100, 2));
    if (auto* dptr = dynamic_cast<Derived*>(someList[0].get()))
        std::cout << dptr->num2 << std::endl;
}

For real code, it's considered better design to make use of virtual functions in Base rather than using lots of if(dynamic_cast) checks.

Upvotes: 2

Related Questions