经过大量研究后,我仍然不明白如何使用智能指针处理抽象类集合。
以下是我遇到的错误:
error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Shape; _Dp = std::default_delete<Shape>]'
base_ptr s = shapes.front();
error: no matching function for call to 'std::unique_ptr<Shape>::unique_ptr(Shape&)'
shapes.push(base_ptr(b));
通过编译最小代码来复制错误(code online avaiable)。
#include <queue>
#include <memory>
class Shape {
public:
virtual int getPerimeter() =0;
};
typedef std::unique_ptr<Shape> base_ptr;
class Circle : public Shape {
public:
virtual int getPerimeter() { return 1; };
};
class Square : public Shape {
public:
virtual int getPerimeter() { return 0; };
};
class ShapeManager {
public:
ShapeManager();
void useShape() {
if(shapes.empty())
throw "Work stack is empty.";
base_ptr s = shapes.front();
s->getPerimeter();
shapes.pop();
}
void submitShape(Shape &b) {
shapes.push(base_ptr(b));
}
private:
std::queue<base_ptr> shapes;
};
int main(int argc, char **argv) {
ShapeManager s();
Circle c;
s.submitShape(c);
s.useShape();
return 1;
}
如果我将queue
声明为queue<Shape*>
,但我不想处理指针 - 意思*。
编辑,此代码编译。感谢大家。 This article建议的Guillaume Racicot有助于让情况更加清晰。
#include <queue>
#include <memory>
class Shape {
public:
virtual int getPerimeter() =0;
};
typedef std::unique_ptr<Shape> base_ptr;
class Circle : public Shape {
public:
Circle() {};
virtual int getPerimeter() { return 1; };
};
class Square : public Shape {
public:
virtual int getPerimeter() { return 0; };
};
class ShapeManager {
public:
ShapeManager();
void useShape() {
if(shapes.empty())
throw "Work stack is empty.";
base_ptr s = std::move(shapes.front());
s->getPerimeter();
shapes.pop();
}
void submitShape(base_ptr b) {
shapes.push(std::move(b));
}
private:
std::queue<base_ptr> shapes;
};
int main(int argc, char **argv) {
ShapeManager s;
base_ptr c = std::make_unique<Circle>();
s.submitShape(std::move(c));
s.useShape();
return 1;
}
答案 0 :(得分:6)
容器分散注意力。问题是unique_ptr
不可复制;如果是的话,它就不会是唯一的。因此,您可能需要添加对std::move
的调用:
base_ptr s = std::move(shapes.front());
这意味着与原始代码可能要做的事情不同;它从容器中删除对象。如果那不是您想要的,那么std::move
不是正确的答案,可能unique_ptr
不是正确的机制。
答案 1 :(得分:3)
您的示例中存在许多问题,而不仅仅是滥用智能指针。首先,最明显的一次是你的s
声明:
ShapeManager s();
这声明了一个名为s
的函数,该函数返回ShapeManager
并且不带参数。
也许您打算声明ShapeManager
类型的对象?
ShapeManager s{};
// Or
ShapeManager s;
其次,你误用了智能指针。你有一个独特的指针队列。唯一指针是围绕免费存储分配对象的RAII包装器。这意味着它是一个使用new
分配的对象构造的包装器。在你的例子中,你没有这样做。您正在使用具有自动存储的对象构建唯一指针。
指向自动存储分配对象的智能指针是观察者指针:不得拥有,删除或尝试管理有关该对象的任何内容。实际上,观察者指针是一种语言特征而不是库特征。它通常被称为指针。
这是使用观察者指针的代码:
template<typename T>
using observer_ptr = T*;
struct ShapeManager {
void useShape() {
if(shapes.empty())
throw "Work stack is empty.";
auto s = shapes.front();
s->getPerimeter();
shapes.pop();
}
void submitShape(Shape &b) {
shapes.push(&b);
}
private:
std::queue<base_ptr> shapes;
};
int main() {
ShapeManager s;
Circle c; // Automatic storage
Rectangle r; // Automatic storage too.
s.submitShape(c);
s.submitShape(r);
s.useShape();
}
但是,您可能不想使用自动存储来保存它们。我的猜测是你想在任何地方使用std::unique_ptr
。这允许提交给形状管理器的对象比它的范围更长。为此,您需要在免费商店分配对象。最常见的方法是使用std::make_unique
:
struct ShapeManager {
void useShape() {
if(shapes.empty())
throw "Work stack is empty.";
// We must create a reference,
// Using simply auto would require copy,
// Which is prohibited by unique pointers
auto&& s = shapes.front();
s->getPerimeter();
shapes.pop();
}
void submitShape(base_ptr b) {
shapes.push(std::move(b));
}
private:
std::queue<base_ptr> shapes;
};
int main() {
ShapeManager s;
// Allocated on the free store,
// The lifetime of c and r are managed by
// The unique pointer.
auto c = std::make_unique<Circle>();
auto r = std::make_unique<Rectangle>();
s.submitShape(std::move(c));
s.submitShape(std::move(r));
s.useShape();
}