Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ prevent object slicing in std::vector

I would like to store multiple classes with the same base class in a std::vector. After some research, it became apparent to me that I have to use pointers to prevent object slicing. However, when I create the vector, add elements to it and return it, the resulting vector doesn't have the right values.

As an example, here are my two classes:

class Base {
    public:
        int var0;
}

class Derived : public Base {
    public:
        int var1;
}

And here is a simple print function. As a rule, all instances of Base should have var0 == 23, and all instances of Derived should have var0 != 23.

void print(Base& value) {
    if (value.var0 == 23) {
        std::cout << "Base: " << value.var0 << std::endl;
    } else {
        Derived d = (Derived&) value;
        std::cout << "Derived: " << d.var0 << ", " d.var1 << std::endl;
    }
}

First of all, this does work like I want it to:

int main() {
    int num = 10;
    std::vector<Base*> vec;

    for (int i = 0; i < num; i++) {
        if (i % 2 == 0) {
            Base b;
            b.var0 = 23;
            vec.push_back(&b);
        } else {
            Derived d;
            d.var0 = 17;
            d.var1 = 42;
            vec.push_back(&d);
        }
    }

    // ....

    for (int i = 0; i < num; i++) {
        print(*vec.at(i));
    }
}

This prints:

Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42

Now, I want this vector to be returned by a function, so I create a function:

std::vector<Base*> createVector(int num) {
    std::vector<Base*> vec;

    for (int i = 0; i < num; i++) {
        if (i % 2 == 0) {
            Base b;
            b.var0 = 23;
            vec.push_back(&b);
        } else {
            Derived d;
            d.var0 = 17;
            d.var1 = 42;
            vec.push_back(&d);
        }
    }

    return vec;
}

int main() {
    int num = 10;
    std::vector<Base*> vec = createVector(num);

    // ....

    for (int i = 0; i < num; i++) {
        print(*vec.at(i));
    }
}

This prints:

Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42

Which is not what I want. I want it to print like the other function did.

Is there any way to solve this problem? Any way to maybe do the whole derived-class thing a bit better?

like image 548
Rheel Avatar asked Apr 07 '26 12:04

Rheel


2 Answers

Your program behaviour is undefined:

Base b;
b.var0 = 23;
vec.push_back(&b);

is pushing back a pointer to a variable (b) that goes out of scope.

Why not use a std::vector<std::unique_ptr<Base>> instead? Object slicing will not be an issue, and the vector will manage the memory for you.

like image 104
Bathsheba Avatar answered Apr 09 '26 02:04

Bathsheba


This has nothing to do with object slicing, and everything to do with you storing pointers to local variables in the vector. Once the scope the variables were declared in has ended, the variables are destructed leaving you with stray pointers, and dereferencing these stray pointers leads to undefined behavior.

You have this problem also in the program that you says work fine. Seemingly working fine is one or the possibilities of undefined behavior.

like image 20
Some programmer dude Avatar answered Apr 09 '26 00:04

Some programmer dude



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!