我正在尝试使用unique_ptr作为子项创建一个树。这是目前的课程:
class Part
{
public:
vector<Part>& getChildren() const {
return *m_children;
}
void attachChild(const unique_ptr<Part>& child) {
m_children.push_back(std::move(child));
}
vector<Part>& getAtoms() const {
vector<Part> atoms;
for (const auto& child : m_children) {
if (child->hasChildren()) {
vector<Part> childChildren = child->getAtoms();
atoms.insert(atoms.end(), childChildren.begin(), childChildren.end());
} else {
atoms.push_back(child);
}
}
return atoms;
}
vector<Part>& getAbsoluteAtoms() const {
vector<Part> atoms;
for (auto child : m_children) { // Not const because I modify the child
if (child->hasChildren()) {
vector<Part> childChildren = child->getAbsoluteAtoms();
atoms.insert(atoms.end(), childChildren.begin(), childChildren.end());
} else {
child.setPosition(child->getPosition() + m_position);
atoms.push_back(child);
}
}
return atoms;
}
private:
vector<unique_ptr<Part>> m_children;
};
我有很多错误,因为指针就像这一个:
D:\Programmes\Qt\Tools\mingw530_32\i686-w64-mingw32\include\c++\bits\stl_construct.h:75: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Part; _Dp = std::default_delete<Part>]'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
没有它们,一切都运作良好,但是当孩子变得非常庞大时,我需要它们。你能告诉我为什么我的代码不正确吗?
答案 0 :(得分:2)
在充分尊重的情况下,我认为你需要研究C ++的基础知识。对我来说,使用此代码推断出您想要实现的目标是不可能的。也许我可以帮助你指出一些错误,因为我认为这应该是你的第一步。
使用std :: move
void attachChild(const unique_ptr<Part>& child) {
m_children.push_back(std::move(child));
}
为什么要移动const&amp;?如果它不是一个恒定的参考,你认为值得移动吗?为什么这是unique_ptr? m_children包含unique_ptr类型的元素,并且您想要使用addChild方法填充该向量?我们可以复制unique_ptr吗?他们不再是独一无二的了。所有这一切都很奇怪。无法理解你的意图。
返回类型
vector<Part>& getAtoms() const { vector<Part> atoms; /* fill atoms */ return atoms; }
您确定要返回对getAtoms()函数末尾被破坏的变量的引用吗?返回类型应该是
vector<Part>
同样在这里:
vector<Part>& getAbsoluteAtoms() const { ... }
需要 vector<unique_ptr<Part>> m_children
?
vector<unique_ptr<Part>> m_children;
我真的很想知道为什么你需要在类属性中存储unique_ptr的向量。我确定你有理由。但在继续之前,我将重新审视C ++的基础知识(复制,引用,指针,移动语义......)。
答案 1 :(得分:0)
您提到的错误告诉您使用unique_ptr
的已删除副本构造函数。它被删除的原因是,顾名思义,必须始终只有一个拥有已分配对象的unique_ptr
。
此复制构造函数的一个用途是在Part
类的默认生成的复制构造函数中,例如,只要您将Part
插入vector<Part>
,就会使用该构造函数。另一个用途是for (auto child : m_children)
循环。
std :: unique_ptr只能移动,这意味着在移动到新的unique_ptr后,源不会引用已分配的对象。由于unique_ptr的目的是确保在所有情况下都能正确删除拥有的对象,并且永远不会删除两次,因此无法盲目复制它。
通过使用std :: unique_ptr,您表示父“拥有”子项,即通过复制父项,您通过删除父项删除子项来复制子项。
这适用于树,因为每个节点最多只有一个父节点,你可以处理根。
如果Part
表示有向非循环图,则可以使用shared_ptr
。
这就是我如何编写具有分层所有权的Part
:
#include <vector>
#include <memory>
class Part
{
public:
// or whatever constructor is useful
Part() = default;
~Part() = default;
// copy constructor needed because we cannot copy unique_ptr
Part(const Part &other) {
// cannot use just auto here, as that would try to copy the unique_ptrs
for(const auto &child_ptr : other.m_children) {
// we recursively copy all children
m_children.emplace_back(std::make_unique<Part>(*child_ptr));
}
}
// move constructor does what we expect
Part(Part && other) = default;
// since we need an explicit copy constructor it is
// good practice to mention also the assignment operators
// see also the "rule of five"
// copy assignment should be similar to the copy constructor, but first clear the children
Part &operator=(const Part &other) {
m_children.clear();
for(const auto &child_ptr : other.m_children) {
// we recursively copy all children
m_children.emplace_back(std::make_unique<Part>(*child_ptr));
}
return *this;
}
// moving should work as expected, we get all the children of other
Part &operator=(Part &&other) = default;
const std::vector<std::unique_ptr<Part>>& getChildren() const {
return m_children;
}
// we take a unique_ptr by value because it (the pointer) is small
void attachChild(std::unique_ptr<Part> child) {
m_children.push_back(std::move(child));
}
bool hasChildren() const {
return !m_children.empty();
}
private:
std::vector<std::unique_ptr<Part>> m_children;
};
// note how we return the vector by value,
// to avoid passing a stale reference
// the user will get a completely new vector
std::vector<Part> makeLotsOfParts(const Part &part) {
std::vector<Part> parts;
for(int i = 0; i < 10; ++i) {
// now we can copy parts!
parts.push_back(part);
}
// here the compiler will either apply the return value optimization
// or move the vector cheaply into the return value
return parts;
}
std::unique_ptr<Part> assemblePart() {
std::unique_ptr<Part> parent = std::make_unique<Part>();
std::unique_ptr<Part> child1 = std::make_unique<Part>();
// we do not need child1 any more, so we move from it
parent->attachChild(std::move(child1));
std::unique_ptr<Part> child2 = std::make_unique<Part>();
parent->attachChild(std::move(child2));
// again we can rely on RVO or move
return parent;
}