Home > database >  Access a string view of an object inside a vector of interfaces C
Access a string view of an object inside a vector of interfaces C

Time:12-18

I am storing different objects which inherit from the same interface inside a single vector. Those objects have their name stored as a constant string view, which gets its value even before the object gets instantiated. I am able to easily retrieve its name before it gets stored in a vector. However, when I store it inside a vector of interfaces and I access its name again, I get undefined behavior (compiler-dependent). How can I fix this?

Here's the simplest case which demonstrates my problem. I'm using a compiler which supports C 17 and some features of C 20

#include <string_view>
#include <vector>
#include <iostream>

struct IFoo {
public:
    virtual ~IFoo() = default;
    const std::string_view name{};
};

struct Foo : public IFoo {
public:
    const std::string_view name = "bar";
};

int main() {
    std::vector<IFoo*> foos;
    Foo *foo = new Foo();
    std::cout << "Name of object before storing: " << foo->name << std::endl;
    foos.push_back(new Foo());
    for (auto foo : foos) {
        std::cout << "Name of object after storing: " << foo->name << std::endl;
    }
}

It produces the following results:

Name of object before storing: bar
Name of object after storing: 

When I tried changing the vector to std::vector<Foo*>, the code ran without any problems. However, this is not a solution, because I want to store multiple types which inherit from the same interface inside a single vector.

CodePudding user response:

Data members can't be overridden. Each name in the base and derived classes is a different member and which one you will name in an expression is determined from the static type of the object in the expression, which is IFoo if you iterate through the vector, but Foo when you refer to foo declared as Foo*.

Only virtual member function have polymorphic behavior. So you need to use one instead:

struct IFoo {
public:
    virtual ~IFoo() = default;
    virtual std::string_view name() const = 0;
};

struct Foo : public IFoo {
public:
    std::string_view name() const override {
        return "bar";
    }
};

//...

for (auto foo : foos) {
    std::cout << "Name of object after storing: " << foo->name() << std::endl;
}

It would also be a good idea to use std::unique_ptr<IFoo> and std::make_unique<Foo> instead of IFoo* and new Foo for memory and exception safety. (Then auto foo will need to be auto& foo instead.)

  • Related