我有一个班级Node
,必须有一个输入Edge
的列表。但是,这些输入Edge
不应由Node
修改,只能被访问。我被建议使用智能指针,如下所示:
class Node
{
private:
std::vector<std::unique_ptr<Edge>> inEdges;
//...
public:
void inline AddEdge(std::unique_ptr<Edge>& edge) // could be const here too
{
this->inEdges.push_back(edge);
}
//...
}
因此,在运行时,我可以创建节点和边的列表,并为每个节点分配边:
int main()
{
std::vector<std::unique_ptr<Nodes>> nodes;
std::vector<std::unique_ptr<Edges>> edges;
nodes.push_back(std::make_unique<Node>(0, 0.5, 0.0));
nodes.push_back(std::make_unique<Node>(1, 0.5, 0.0));
edges.push_back(std::make_unique<Edge>(*nodes[0], *nodes[1], 1.0));
nodes[1]->AddEdge(edges[0]);
}
编译器提供错误
错误1错误C2280:'std :: unique_ptr&gt; :: unique_ptr(const std :: unique_ptr&lt; _Ty,std :: default_delete&lt; _Ty&gt;&gt;&amp;)':尝试引用已删除的函数c:\ program文件(x86)\ microsoft visual studio 12.0 \ vc \ include \ xmemory0 593 1 sirs
由于Node
的签名为std::vector<Edge*>
,因此AddEdge
void AddEdge(Edge& edge);
内的原始指针处理&edge
在矢量中。
我的代码出了什么问题?我该如何处理呢?鉴于std::vector
无法存储引用,因为它们不可分配。
PS:我不想将指针的所有权转移到Node
对象......
答案 0 :(得分:4)
std::vector::push_back
复制其参数,std::unique_ptr
无法复制,只能移动。
您必须移动传递的std::unique_ptr
:
void inline AddEdge(std::unique_ptr<Edge> edge)
{
this->inEdges.push_back(std::move(edge));
}
然后这样称呼它:
nodes[1]->AddEdge(std::move(edges[0]));
此外,inEdges
并非必须是std::unique_ptr
的向量,因为它们应该是非拥有者。 std::unique_ptr
意味着所有权,但正如您所说,Node
并不拥有Edge
。
使用std::vector<Edge*>
代替(或std::observer_ptr
在不久的将来实施时),因为原始指针(通常)意味着非所有权。您必须要小心std::vector<Edge*>
的生命周期超过std::unique_ptr
的生命周期,否则指针将悬空。
答案 1 :(得分:4)
您应该移动 std::unique_ptr
的实例,以便将它们放入std::vector
。否则你需要std::shared_ptr
。
class Node
{
private:
std::vector<std::unique_ptr<Edge>> inEdges;
//...
public:
void AddEdge(std::unique_ptr<Edge>&& edge)
{
inEdges.push_back(std::move(edge));
}
//...
}
我已更改AddEdge
以获取r值参考,以便正确支持移动语义。
致电:
node.AddEdge(std::move(edge));
或者:
node.AddEdge(std::make_unique<Edge>(/*args*/));
暂且不说。如果您确实发现通过引用传递了std::unique_ptr
,则可能是您应该使用std::shared_ptr
的标志。
此外,inline
对于在类中声明的方法是多余的。
答案 2 :(得分:2)
我根本不会使用智能指针。我认为最简单的方法是将所有节点和 edge 存储为主向量中的值,然后使用原始指针表达他们的关系。
您不需要智能指针,因为向量管理节点和边缘的生命周期和破坏 >所以没有别的东西需要拥有它们。
使用此方案时要记住的最重要的事情是,在使用的数据结构之后,您必须从不更改向量的大小已创建指向其元素的指针。
原因是更改向量的大小会使所有指针无效(对象可能会移动到不同的内存位置)。
这样的事情:
class Edge
{
class Node* a;
class Node* b;
double w;
public:
Edge(Node* a, Node* b, double w): a(a), b(b), w(w) {}
};
class Node
{
private:
double x, y, z;
std::vector<Edge*> inEdges;
public:
Node(double x, double y, double z): x(x), y(y), z(z) {}
void AddEdge(Edge* edge)
{
this->inEdges.push_back(edge);
}
};
int main()
{
std::vector<Node> nodes; // values, not pointers
std::vector<Edge> edges;
// NOTE: You MUST create ALL of the Nodes BEFORE
// adding their pointers to the Edges and you must
// create ALL the Edges BEFORE adding their pointers
// to the Nodes because resizing the vectors will
// INVALIDATE all the pointers
// FIRST create ALL the nodes
nodes.emplace_back(0, 0.5, 0.0); // make nodes[0]
nodes.emplace_back(1, 0.5, 0.0); // make nodes[1]
// NEXT create ALL the edges
edges.emplace_back(&nodes[0], &nodes[1], 1.0); // make edges[0]
// Finally add the edges to the nodes
nodes[1].AddEdge(&edges[0]);
}
答案 3 :(得分:1)
您正在尝试将 std :: unique_ptr 的副本推送到向量中。该向量试图创建unique_ptr的副本,这是不允许的,因为它会破坏独特所有权的目的。
您应首先考虑哪个对象拥有哪个对象,并且只有在确实存在唯一所有权的情况下才使用 std :: unique_ptr 。
您可以做几件事:
在你的情况下阅读 std :: weak_ptr 可能是值得的,因为我认为你可能希望对象相互引用,而不创建循环引用。弱指针在没有实际增加引用计数的情况下对shared_ptr进行处理,并且在某些情况下,您希望确保对象不会长时间挂在引用上并造成内存泄漏。
希望这有帮助。