简体   繁体   中英

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?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM