对多态对象成员的无效引用

Invalidated references to members of polymorphic objects

本文关键字:无效 引用 成员 多态 对象      更新时间:2023-10-16

假设我有两个类FooBar继承接口Base,并希望将这些类的实例存储在类型为std::vector<std::unique_ptr<Base>>的向量中。Bar类型的对象应该存储对值的引用(在本场景中,该值是Foo类的成员)。从概念上讲,我想实现以下目标:

#include <iostream>
#include <memory>
#include <vector>
template<typename Type, typename ... Types>
std::unique_ptr<Type> make_unique(Types &&... arguments) {
    return std::unique_ptr<Type>(new Type(std::forward<Types>(arguments)...));
}
class Base {
public:
    virtual ~Base() = 0;
    virtual void execute() = 0;
};
Base::~Base() {
}
class Foo: public Base {
public:
    void execute() override {
        // Foo-specific implementation of execute(), may change value_
        value_ = 2;
    }
    const int & value() const { 
        return value_; 
    }
private:
    int value_;
};
class Bar: public Base {
public:
    Bar(const int &value) : value_(value) {}
    void execute() override {
        // Bar-specific implementation of execute(), uses current value_
        std::cout << value_ << std::endl;
    }
private:
    const int &value_; // Reference to a value, not necessarily from Foo
};
int main() {
    // Collection of base objects, possibly inside another object
    std::vector<std::unique_ptr<Base>> baseVector;
    baseVector.emplace_back(make_unique<Foo>());
    baseVector.emplace_back(make_unique<Bar>(
        dynamic_cast<Foo *>(baseVector.back().get())->value()));
    for (auto &base : baseVector)
        base->execute();
    return 0;
}

然而,dynamic_cast对我来说很臭(我知道我也可以使用static_cast,但我想这并没有好到哪里去)。避免dynamic_cast的另一种选择是将Foo类更改为类似

class Foo: public Base {
public:
    Foo() : value_(new int()) {}
    void execute() override {
        // Foo-specific implementation of execute(), may change value_
        *value_ = 2;
    }
    const int & value() const { 
        return *value_; 
    }
private:
    std::unique_ptr<int> value_;
};

然后做

int main() {
    // Collection of base objects, possibly inside another object
    std::vector<std::unique_ptr<Base>> baseVector;
    auto foo = make_unique<Foo>();
    auto *fooPtr = foo.get();
    baseVector.emplace_back(std::move(foo));
    baseVector.emplace_back(make_unique<Bar>(fooPtr->value()));
    for (auto &base : baseVector)
        base->execute();
    return 0;
}

但这似乎也不是很优雅。如何以适当的方式处理此类情况?

我会用一种更通俗易懂的方式写它:

struct Base { virtual ~Base() = default; /* ...*/ };
struct Foo : Base { int x_; int & value() { return x_; /* ...*/ };
struct Bar : Base { int & r_; Bar(int & r) : r_(r) {} /* ...*/ };
auto foo = std::make_unique<Foo>();
auto bar = std::make_unique<Bar>(foo->value());
v.push_back(std::move(foo));
v.push_back(std::move(bar));

仅仅因为emplace_back的存在并不意味着你必须把它用于所有事情!