Can't avoid object slicing in c++

377 Views Asked by At

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 !
}

1

There are 1 best solutions below

2
aschepler On

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.