Я пытался создать фабричный метод, который выделяет сущности подклассов из заданного абстрактного класса, который я для простоты назову Shape
. Это выглядит примерно так:
Shape* makeChild(Type type) const
{
switch(type)
{
case QUAD:
return new Quad();
case RECTANGLE:
return new Rectangle();
case TRIANGLE:
return new Triangle();
default:
return new Rectangle();
}
}
Теперь я пытаюсь сохранить эти сущности внутри структуры, и я понимаю, что мне нужно выделить Shape*
сущности, но я хотел бы, чтобы объекты автоматически управлялись с помощью вместо этого std::unique_ptr<Shape>
.
Моя структура ShapeTree
выглядит примерно так:
struct ShapeTree {
int index_;
std::vector<std::unique_ptr<Shape>> shapes_;
void add(Shape* shape) {shapes_.push_back(std::make_unique<Shape>(shape));}
void removeLast(){shapes_.pop_back();}
ShapeTree(const int index) : index_{index}{}
ShapeTree(const int index, std::vector<std::unique_ptr<Shape>>& shapes) : index_{index}, shapes_{std::move(shapes)}{}};
Компилятор жалуется на то, что конструктор копирования Shape по умолчанию помечен как удаленный, я понимаю, что это так, поскольку у меня есть уникальный указатель, и я не могу его скопировать, но удаление конструктора копирования и присваивания и использование конструктора перемещения по умолчанию и присвоение победит тоже не работает. Он также жалуется, что экземпляры Shape
не могут быть созданы, поскольку они абстрактны, даже если я возвращаю только подклассы.
Другая версия той же структуры прекрасно работает при использовании необработанных указателей. Например.
struct ShapeTree {
int index_;
std::vector<Shape*> shapes_;
void add(Shape* shape) {shapes_.push_back(shape);}
void removeLast(){shapes_.pop_back();}
ShapeTree(const int index) : index_{index}{}
ShapeTree(const int index, std::vector<Shape*>& shapes) : index_{index}, shapes_{std::move(shapes)}{}};
Что я делаю не так? Как я могу добиться того же результата, используя unique_ptr
?
Измените makeChild, чтобы он возвращал unique_ptr в базу, std::unique_ptr<Shape> makeChild
и создание дочернего элемента должно выполняться не с помощью new, а с помощью. std::make_unique<Quad>
(и прямоугольник и т. д.). Затем вам нужно использовать push_back(std::move(shape))
для переноса формы в контейнер (где фигура — это std::unique_ptr<Shape>). Когда контейнер удаляется, все фигуры указывают на него.
Если возможно, я бы подумал о том, чтобы Shape* makeChild(Type type) const
возвращал std::unique_ptr<Shape>
, чтобы вы всегда защищали память. Нравится std::unique_ptr<Shape> makeChild(Type type) const
Непосредственной причиной вашей ошибки является эта строка:
void add(Shape* shape) {shapes_.push_back(std::make_unique<Shape>(shape));}
std::make_unique
используется для создания нового объекта, которым будет управлять unique_ptr
.
В вашем случае вы уже выделили свой объект с помощью new
, поэтому вам следует просто использовать конструктор std::unique_ptr
.
Немедленным решением будет:
//----------------------------------------vvvvvvvvvvvvvvv-----------------
void add(Shape* shape) {shapes_.push_back(std::unique_ptr<Shape>(shape));}
Однако:
Лучшее решение — вообще не использовать new
.makeChild
может вернуть std::unique_ptr
, который может быть move
d в vector
:
#include <vector>
#include <memory>
struct Shape {};
struct Rectangle : public Shape {};
// ... all the derived Shapes
std::unique_ptr<Shape> makeChild()
{
// Here you can create a std::unique_ptr to any derive from Shape based on a `Type` parameter you can add:
return std::make_unique<Rectangle>();
}
struct ShapeTree {
int index_;
std::vector<std::unique_ptr<Shape>> shapes_;
//---------------------------------------------------------vvvvvvvvv------
void add(std::unique_ptr<Shape> shape) { shapes_.push_back(std::move(shape)); }
void removeLast() { shapes_.pop_back(); }
// ...
};
int main()
{
ShapeTree st{ 333 };
st.add(makeChild());
}
Спасибо всем!! В общем, ошибка была с моей стороны. Меня смутил синтаксис std::make_unique и std::unique_ptr. Я изменил фабричный метод, чтобы он возвращал unique_ptr, и теперь он работает как шарм, мой единственный вопрос в том, должен ли я теперь удалить конструктор копирования и оператор присваивания для Shape или он удаляется по умолчанию?
Не уверен, что понимаю. Вы нам тоже не показали Shape
. Возможно, вы можете опубликовать его как новый вопрос со всеми соответствующими деталями (сообщение в StackOverflow обычно должно быть ограничено одной проблемой).
В
add
:std::make_unique
для создания нового объекта. Вы уже создали объект с помощьюnew
, поэтому вам следует использоватьstd::unique_ptr<Shape>(shape)
. Вам следует вообще избегатьnew
—makeChild
может вернутьstd::unique_ptr
черезstd::make_unique
.