使用 unique_ptrs 的基本转发列表
Basic forward list using unique_ptrs
作为学习 C++ 的练习,我想使用原始指针和 unique_ptrs 构建我自己的前向列表。使用原始指针,我有:
struct node_raw {
node_raw(int data_, node_raw *next_) : data(data_), next(next_) {}
int data;
node_raw *next;
};
那我就可以这样写了
int main() {
node_raw r1{1, nullptr};
node_raw r2{2, &r1};
node_raw r3{3, &r2};
}
获取转发列表:r3 -> r2 -> r1。
现在我想用 unique_ptrs 做同样的事情。
struct node_unique {
node_unique(int data_, std::unique_ptr<node_unique> next_)
: data(data_), next(std::move(next_)) {}
int data;
std::unique_ptr<node_unique> next;
};
这是我目前的客户端代码:
int main() {
node_unique u1{1, nullptr};
node_unique u2{2, std::make_unique<node_unique>(std::move(u1))};
node_unique u3{3, std::make_unique<node_unique>(std::move(u2))};
}
这给出了 u3.next->data = 2
,所以它似乎有效。但这是对的吗?为什么我需要 std::move
两次才能创建一个新节点?现在查询 u1.data
是不是因为已经被移走了?基本上,使用 unique_ptrs 实现最小转发列表的规范方法是什么?
Why do I need std::move twice to make a new node?
您无法复制 node_unique
,因为它已经隐式删除了 std::unique_ptr
成员的复制构造函数。
Is it now invalid to query u1.data because it has been moved from?
没有。隐式生成的移动构造函数将复制成员。读取复制自的int
成员不无效。
But is it right?
节点 class 本身没有大问题1,但是您使用它的方式可能会产生误导。 u1
和 u2
不是根为 u3
的列表的一部分。列表节点是 moved-from u1
和 u2
.
的浅拷贝
您不一定需要变量;由于列表元素是动态的,您可以直接创建它们:
node_unique root (
3,
std::make_unique<node_unique>(
2,
std::make_unique<node_unique>(
1,
nullptr
)
)
);
Basically, what is the canonical way to implement a minimal forward list using unique_ptrs?
“最小”是相对于您的要求而言的。您可以通过删除构造函数使节点成为 aggregate.
来使其更小
我不会说有一种“规范”的方式来实现转发列表,但它应该符合 Container
的概念。然而,那将是最小的。
1 除了析构函数具有线性递归深度这一事实外,正如 .
所指出的,这会导致中等大小列表的堆栈溢出
您应该实现一个自定义析构函数来迭代地销毁节点。这需要一点思考来防止智能指针析构函数钻研递归。
作为学习 C++ 的练习,我想使用原始指针和 unique_ptrs 构建我自己的前向列表。使用原始指针,我有:
struct node_raw {
node_raw(int data_, node_raw *next_) : data(data_), next(next_) {}
int data;
node_raw *next;
};
那我就可以这样写了
int main() {
node_raw r1{1, nullptr};
node_raw r2{2, &r1};
node_raw r3{3, &r2};
}
获取转发列表:r3 -> r2 -> r1。
现在我想用 unique_ptrs 做同样的事情。
struct node_unique {
node_unique(int data_, std::unique_ptr<node_unique> next_)
: data(data_), next(std::move(next_)) {}
int data;
std::unique_ptr<node_unique> next;
};
这是我目前的客户端代码:
int main() {
node_unique u1{1, nullptr};
node_unique u2{2, std::make_unique<node_unique>(std::move(u1))};
node_unique u3{3, std::make_unique<node_unique>(std::move(u2))};
}
这给出了 u3.next->data = 2
,所以它似乎有效。但这是对的吗?为什么我需要 std::move
两次才能创建一个新节点?现在查询 u1.data
是不是因为已经被移走了?基本上,使用 unique_ptrs 实现最小转发列表的规范方法是什么?
Why do I need std::move twice to make a new node?
您无法复制 node_unique
,因为它已经隐式删除了 std::unique_ptr
成员的复制构造函数。
Is it now invalid to query u1.data because it has been moved from?
没有。隐式生成的移动构造函数将复制成员。读取复制自的int
成员不无效。
But is it right?
节点 class 本身没有大问题1,但是您使用它的方式可能会产生误导。 u1
和 u2
不是根为 u3
的列表的一部分。列表节点是 moved-from u1
和 u2
.
您不一定需要变量;由于列表元素是动态的,您可以直接创建它们:
node_unique root (
3,
std::make_unique<node_unique>(
2,
std::make_unique<node_unique>(
1,
nullptr
)
)
);
Basically, what is the canonical way to implement a minimal forward list using unique_ptrs?
“最小”是相对于您的要求而言的。您可以通过删除构造函数使节点成为 aggregate.
来使其更小我不会说有一种“规范”的方式来实现转发列表,但它应该符合 Container
的概念。然而,那将是最小的。
1 除了析构函数具有线性递归深度这一事实外,正如
您应该实现一个自定义析构函数来迭代地销毁节点。这需要一点思考来防止智能指针析构函数钻研递归。