Скажут, у меня есть два класса 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;
}
Но и это, кажется, не очень элегантно. Как справляться с подобными ситуациями надлежащим образом?
['std :: make_unique'] (http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) - это C++ 14. – Zereges
@Zereges: Поскольку 'std :: make_unique' является C++ 14, я предоставил свою собственную реализацию' make_unique'. – Marcel
@ Kerrek SB: Хорошо, теперь вы выглядите немного более элегантно. Но ваше решение, а также моя вторая альтернатива требуют, чтобы член 'value_' класса' Foo' имел тип 'std :: unique_ptr'. В противном случае ссылка станет недействительной, как только экземпляр «Foo» будет перенесен в вектор. Является ли 'std :: unique_ptr' действительно моим единственным вариантом здесь? –
Marcel