标签:
template<typename T>
class Queue
{
private:
struct Node
{
T data;
std::unique_ptr<T> next = nullptr;
Node(T _data):
data(std::move(_data))
{}
};
std::unique_ptr<Node> head = nullptr;
Node* tail = nullptr;
public:
Queue() = default;
Queue(const Queue&) = delete;
Queue& operator=(const Queue&) = delete;
std::shared_ptr<T> tryPop()
{
if (head == nullptr){
return std::shared_ptr<T>();
}
auto const result = std::make_shared<T>(std::move(head->data));
auto const oldHead = std::move(head);
head = std::move(oldHead->next);
return result;
}
void push(T newVal)
{
auto newNextNode = std::make_unique<Node>(std::move(newVal));
auto newTail = newNextNode.get ();
if (tail != nullptr){
tail->next = std::move(newNextNode);
}
else{
head = std::move(newNextNode);
}
tail = newTail;
}
};
我也曾怀疑,为何不把 tail 也作为一个 unique_ptr,所以使用了一个反证法。若 tail 的类型是一个 unique_ptr,那么当 push 一个新值的时候,接下来的操作应该是:
auto newTail = make_unique<T>(newValue); tail->next = move(newTail); tail = move(newTail); // oops!
问题就出在第三行。怎么说呢,如果是拷贝语义,那么是没错的,然而使用的是 move,这样在使用了第一个 move 操作之后,newTail 就会被析构,第二个 move 移动的是一个 nullptr。
当然,如果全部使用裸指针倒是可以这样:
auto newTail = new Node(newValue); // 新结点
tail->next = newTail; // 新节点成为现 tail 的 next
tail = newTail; // 让 tail 指向真正的新节点
倒是达到了效果,但是对于其资源的释放又成了一个棘手的问题,因此把 head 和 tail 的类型分别设为 unique_ptr 和 Node* 就有意义了。添加元素时,为了新节点的信息得以保存,先根据添加的值构造一个 unique_ptr 来代替原先 tail 的 next,又因为在使用 move 移动这个 unique_ptr 之后,其本身会被析构,所以在代替之前,需要先使用 get 得到新链表节点的信息然后再在最后让 tail 指向真正的队尾。
标签:
原文地址:http://www.cnblogs.com/wuOverflow/p/4832256.html