假设我有两个类Foo
和Bar
继承接口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
的存在并不意味着你必须把它用于所有事情!