如何在树上像孩子一样使用unique_ptr



我正在尝试用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:ProgrammesQtToolsmingw530_32i686-w64-mingw32includec++bitsstl_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)...); }
^

没有它们,一切都会很好,但作为一个孩子,我可以变得非常巨大,我需要他们。你能告诉我为什么我的代码不正确吗?

恕我直言,我认为您需要学习C++的基础知识。对我来说,不可能推断出你想用这段代码实现什么。也许我可以帮你指出一些错误,因为我相信这应该是你的第一步。

错误地使用标准::移动

void attachChild(const unique_ptr<Part>& child) {
m_children.push_back(std::move(child));
}

你为什么要移动一个康斯特&?如果它不是一个恒定的参考,你认为值得移动它吗?为什么这是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++的基础知识(复制、引用、指针、移动语义......

您提到的错误告诉您使用的是已删除的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;    
}

最新更新