如何将此旧的C 双重链接列表代码更新为C 11
How do I update this old C++ doubly linked list code to C++11?
我在现在称为 C++98
它实现了双重链接列表,我正在考虑将其更新为C++11
,但是我有以下问题:
- 似乎使用
std::unique_ptr
是有道理的,但是仍然需要其他指示"共享" unique_ptr。即,如果nxt
是唯一的_ptr,我该如何处理prv
和iterator::it
? - 似乎应该将分配器作为模板副手,但它将是
T
类型的分配者,而不是llnode? - 是否有任何特征,需要"注册"?
- 如果使用
move
语义,则应定义哪些方法,例如operator=() &&
等? - 需要更正什么不确定的行为(如果有)?
- 我不担心自己应该是什么?
一个公认的答案将简要讨论问题,然后在C++11
中重新进来(诚然,这是很多代码,但大多数都会被剪切和粘贴)
一个好的答案将简要讨论问题,然后在C++11
中重新进来,以说明如何应用这些概念
使用标准算法使用标准容器破坏了目的
是有意义的在有意义的情况下,鼓励使用C 11功能,例如foreach
而不是仅使用lambdas
,因为它将起作用
这不是作业
代码:
template <class T>
class LinkedList
{
class LLNode
{
public:
LLNode() // For Sentinel (requires that T has a default constructor)
{} // set prv/nxt directly
explicit LLNode(const T& x) : data(x) // For normal nodes
{} // set prv/nxt directly
T& get_data() const
{
return const_cast<T&>(data);
}
LLNode * prv;
LLNode * nxt;
private:
T data;
};
public:
class iterator
{
public:
iterator(const LinkedList * p, LLNode * i)
: parent(const_cast<LinkedList *>(p)), it(i)
{}
iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
iterator operator ++(int) // post
{
iterator ret=*this;
it = it->nxt;
return ret;
}
iterator& operator --() // pre
{
it = it->prv;
return *this;
}
iterator operator --(int) //post
{
iterator ret=*this;
it = it->prv;
return ret;
}
T& operator *() const
{
return it->get_data();
}
T * operator ->() const
{
return &(it->get_data());
}
bool operator ==(const iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const iterator& rhs) const
{
return it != rhs.it;
}
void erase()
{
parent->remove(it);
}
void insert_after(const T& x)
{
LLNode * add= new LLNode(x);
parent->insert(it, add);
}
void insert_before(const T& x)
{
LLNode * add= new LLNode(x);
parent->insert(it->prv, add);
}
private:
LinkedList * parent;
LLNode * it;
};
// Linked List class definition
LinkedList()
{
init();
}
LinkedList(const LinkedList& rhs)
{
init();
cp(rhs);
}
~LinkedList()
{
rm();
}
LinkedList operator =(const LinkedList& rhs)
{
if (this != &rhs)
{
rm();
cp(rhs);
}
return *this;
}
iterator begin() const
{
return iterator(this, sentinel.nxt);
}
iterator rbegin() const
{
return iterator(this, sentinel.prv);
}
iterator end() const
{
return iterator(this, const_cast<LLNode *>(&sentinel));
}
T& get_first() const // illegal if is_empty() is true
{
return sentinel.nxt->get_data();
}
T& get_last() const // illegal if is_empty() is true
{
return sentinel.prv->get_data();
}
size_t size() const
{
return count;
}
bool is_empty() const
{
return count==0;
}
void insert_first(const T& x)
{
LLNode * add= new LLNode(x);
insert(&sentinel, add);
}
void insert_last(const T& x)
{
LLNode * add= new LLNode(x);
insert(sentinel.prv, add);
}
void erase_first() // illegal if is_empty() is true
{
remove(sentinel.nxt);
}
void erase_last() // illegal if is_empty() is true
{
remove(sentinel.prv);
}
private:
void insert(LLNode * before, LLNode * added)
{
LLNode * after=before->nxt;
added->prv=before;
added->nxt=after;
before->nxt=added;
after->prv=added;
++count;
}
void remove(LLNode * node) // illegal if is_empty() is true
{
node->prv->nxt=node->nxt;
node->nxt->prv=node->prv;
delete node;
--count;
}
void cp(const LinkedList& rhs)
{
for (iterator i=rhs.begin(); i != rhs.end(); ++i)
{
insert_last(*i);
}
}
void rm()
{
LLNode * run=sentinel.nxt;
while (run != &sentinel)
{
LLNode * dead=run;
run=run->nxt;
delete dead;
}
}
void init()
{
count=0;
sentinel.nxt = sentinel.prv = &sentinel; // setup circular ref
}
LLNode sentinel;
size_t count;
};
编辑-C 11根据Mooing Duck的答案尝试:
template <class T, class ALLOC=std::allocator<T> >
class LinkedList
{
struct LLNode
{
LLNode * prv;
LLNode * nxt;
T& get_data() { return data; }
T data;
};
public:
class iterator
{
public:
using difference_type = ptrdiff_t;
using value_type = T;
using reference = T&;
using pointer = T*;
using iterator_category = std::bidirectional_iterator_tag;
iterator(LinkedList * p, LLNode * i) : parent(p), it(i)
{}
iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
iterator operator ++(int) // post
{
iterator ret=*this;
it = it->nxt;
return ret;
}
iterator& operator --() // pre
{
it = it->prv;
return *this;
}
iterator operator --(int) //post
{
iterator ret=*this;
it = it->prv;
return ret;
}
const T& operator *() const
{
return it->get_data();
}
T& operator *()
{
return it->get_data();
}
const T * operator ->() const
{
return &(it->get_data());
}
T * operator ->()
{
return &(it->get_data());
}
bool operator ==(const iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const iterator& rhs) const
{
return it != rhs.it;
}
void erase()
{
parent->remove(it);
}
void insert_after(T& x)
{
auto add=parent->alloc_node(x);
parent->insert(it->nxt, add);
}
void insert_before(T& x)
{
auto add=parent->alloc_node(x);
parent->insert(it, add);
}
private:
LinkedList * parent;
LLNode * it;
};
class const_iterator
{
public:
using difference_type = ptrdiff_t;
using value_type = const T;
using reference = const T&;
using pointer = const T*;
using iterator_category = std::bidirectional_iterator_tag;
const_iterator(const LinkedList * p, const LLNode * i) : parent(p),
it(const_cast<LLNode *>(i))
{}
const_iterator(iterator& cvt) : parent(cvt.parent), it(cvt.it)
{}
const_iterator& operator ++() // pre
{
it = it->nxt;
return *this;
}
const_iterator operator ++(int) // post
{
const_iterator ret=*this;
it = it->nxt;
return ret;
}
const_iterator& operator --() // pre
{
it = it->prv;
return *this;
}
const_iterator operator --(int) //post
{
const_iterator ret=*this;
it = it->prv;
return ret;
}
const T& operator *() const
{
return it->get_data();
}
const T * operator ->() const
{
return &(it->get_data());
}
bool operator ==(const const_iterator& rhs) const
{
return it == rhs.it;
}
bool operator !=(const const_iterator& rhs) const
{
return it != rhs.it;
}
private:
const LinkedList * parent;
LLNode * it;
};
using my_alloc=typename
std::allocator_traits<ALLOC>::template rebind_alloc<LLNode>;
using my_traits=typename
std::allocator_traits<ALLOC>::template rebind_traits<LLNode>;
// Linked List class definition
LinkedList(const ALLOC& alloc = ALLOC() ) : mem(alloc)
{
init();
}
LinkedList(const LinkedList& rhs) : mem(rhs.mem)
{
init();
cp(rhs);
}
LinkedList(LinkedList&& rhs) : mem(rhs.mem) // Move
{
init();
shallow_cp(rhs);
}
~LinkedList()
{
rm();
}
LinkedList operator =(const LinkedList& rhs)
{
if (this != &rhs)
{
rm();
cp(rhs);
}
return *this;
}
LinkedList operator =(LinkedList&& rhs) // Move
{
if (this != &rhs)
{
rm();
shallow_cp(rhs);
}
return *this;
}
const_iterator begin() const
{
return const_iterator(this, sentinel.nxt);
}
iterator begin()
{
return iterator(this, sentinel.nxt);
}
const_iterator rbegin() const
{
return const_iterator(this, sentinel.prv);
}
iterator rbegin()
{
return iterator(this, sentinel.prv);
}
const_iterator end() const
{
return const_iterator(this, &sentinel);
}
iterator end()
{
return iterator(this, &sentinel);
}
T& front() // illegal if is_empty() is true
{
return sentinel.nxt->get_data();
}
T& back() // illegal if is_empty() is true
{
return sentinel.prv->get_data();
}
size_t size() const
{
return count;
}
bool is_empty() const
{
return count==0;
}
void insert_first(const T& x)
{
LLNode * add=alloc_node(x);
insert(&sentinel->nxt, add);
}
void insert_last(const T& x)
{
LLNode * add=alloc_node(x);
insert(&sentinel, add);
}
void erase_first() // illegal if is_empty() is true
{
remove(sentinel.nxt);
}
void erase_last() // illegal if is_empty() is true
{
remove(sentinel.prv);
}
private:
LLNode * alloc_node(const T& x)
{
auto ret = my_traits::allocate(mem,1);
my_traits::construct(mem, &(ret->data), x);
return ret;
}
void unalloc_node(LLNode * dead)
{
my_traits::deallocate(mem, dead, 1);
}
void insert(LLNode * after, LLNode * added)
{
LLNode * before=after->prv;
added->prv=before;
added->nxt=after;
before->nxt=added;
after->prv=added;
++count;
}
void remove(LLNode * node) // illegal if is_empty() is true
{
node->prv->nxt=node->nxt;
node->nxt->prv=node->prv;
unalloc_node(node);
--count;
}
void cp(const LinkedList& rhs)
{
mem = rhs.mem;
for (const_iterator i=rhs.begin(); i != rhs.end(); ++i)
{
insert_last(*i);
}
}
void shallow_cp(LinkedList& rhs)
{
if (rhs.count)
{
count=rhs.count;
sentinel=rhs.sentinel; // shallow copy
// fix the links to the old sentinel
sentinel.nxt.prv=&sentinel;
sentinel.prv.nxt=&sentinel;
rhs.init();
}
}
void rm()
{
LLNode * run=sentinel.nxt;
while (run != &sentinel)
{
LLNode * dead=run;
run=run->nxt;
unalloc_node(dead);
}
}
void init()
{
count=0;
sentinel.nxt = sentinel.prv = &sentinel; // setup circular ref
}
LLNode sentinel;
size_t count;
my_alloc mem;
};
是否缺少/错误?
似乎使用std :: unique_ptr是有道理的,但是仍然需要其他指示"共享" unique_ptr。即,如果nxt是唯一的_ptr,我该如何处理prv和iterator :: it?
不要。(1)在不小心删除节点的情况下执行各种内部算法,并且(2)unique_ptr存储了deleter,在您的情况下,它是(a)迭代器的副本,或(b)指向迭代器的指针。两者都是浪费空间。容器应存储分配器,并且容器应处理删除。
看来应该将分配器作为模板派材,但它将是T型的分配者,而不是llnode?
容器采用T型的分配器,尽管它们都在内部使用篮板类型。标准是对容器的分配使用T
型,因此每个std::vector<T, allocator<?>>
都匹配彼此之间的匹配。另外,局外人应该无法访问llnode。不过,您可能会在内部存储given_allocator<LLNode>
。这就是为什么我们要重新结束。
是否有任何特征,需要"注册"?
对于容器,没有。有匹配的接口,但是相对明显。
是的,您的迭代器应该注册性状,但这很容易通过在前面添加五个Typedef来完成。
typedef ptrdiff_t difference_type; //usually ptrdif_t
typedef T value_type; //usually T
typedef T& reference; //usually T&
typedef T* pointer; //usually T*
typedef std::bidirectional_iterator_tag iterator_category;
如果使用移动语义,则应定义哪些方法,例如操作员=()&amp;&amp;等等?
显然,如果有意义的话,容器应移动并可以分配,这是99.99%的时间。甚至std::array
也有移动操作员。还确定哪些成员函数应支持仅移动-T(通过迭代器插入范围,而不是插入范围 计数),哪些成员函数支持 any thy t(emplace One)。
哪些未定义的行为(如果有)需要纠正?
看来,您的
insert(iterator, data)
在迭代器之后插入数据,当标准是在迭代器之前插入时。您的方式使得不可能在开始时添加数据,并可以在末期之后添加数据。您的迭代器具有,但不需要
remove
和insert
功能。我不会提到它,但是在这种情况下,他们要求迭代器的两倍。我会提供暂定的建议来删除它们,但只是试探性的。这是一个很小的罚款,也可能是一个有用的功能。您的
operator =
汇总所有内容,然后重新关注。在可能的情况下简单地复制元素可能会很方便。
编码更棘手您缺少从一对迭代器构造的构造函数。
您的迭代器允许从
const
容器中突变数据。这个是大get_first
和get_last
通常称为front
和back
。
我不担心自己应该是什么?
每个人都俯瞰的大型是例外。您的代码是的例外安全,但这似乎是因为您的代码非常简单,并且您跳过了所有"硬"部分:P
有关更多信息,请审查以下内容:
编写自己的STL容器
如何实现STL风格的迭代器并避免常见的陷阱?
编辑您的C 11随访:
缺少:
- 模板llnode(u&amp;&amp; x)//或一个
T&&
和一个const T&
- linkedlist(std :: prinitizer_list x)
- t&amp;llnode :: get_data()const//重要,您的代码甚至不会编译
- t* iterator ::操作员 ->()const//重要
- const_iterator cbegin()const
- const_iterator cend()const
- const t&amp;front()const//重要
- const t&amp;back()const//重要
- 模板void分配(iter,iter);
- void分配(std :: prinitizer_list);
- void交换(const x&amp;);
不正确:
- insert_*可能应服用
U&&
并使用std::forward<U>(x)
。variadic模板会更好。 - 否迭代对构造函数。
- 内存泄漏在alloc_node中,您应该构造一个llnode,您的prv/nxt。
- unalloc_node永远不会摧毁,只会出现,导致内存泄漏
缺少但不重要
-
typedef std::reverse_iterator<iterator> reverse_iterator;
-
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- reverse_iterator rbegin();//可选
- const_reverse_iterator rbegin()const;
- const_reverse_iterator crbegin()const;
- reverse_iterator rend();//可选
- const_reverse_iterator rend()const;
- const_reverse_iterator crend()const;
可选:
- 不确定为什么
my_alloc
和my_traits
并且它们相同。 - 从分配器私下继承。这有点奇怪,但通常可以节省8个字节。
- 从链接列表c++中删除一个项目
- 读取文件的最后一行并输入到链接列表时出错
- 下面是我为检测链接列表中的循环而制作的代码
- 有人能帮我处理这个链接列表吗?C++
- C++ 创建包含链表和字符串的对象的链接列表时出错
- 你能检查一下为什么在这个代码中从链接列表中删除项目不起作用吗
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 链接列表运算符重载没有打印出我想要的内容
- 链接列表在 cpp 中包含不同的对象类
- 创建一个棋盘格或"Interweave"两个链接列表。IE 更改两个链表的指针
- 如何在构建链接列表时调整头、尾指针
- Shared_ptr双链接列表内存泄漏
- 为什么每当我尝试运行此链接列表删除功能时都会收到分段错误错误?
- 在链接列表中查找元素 - C++
- 删除链接列表中剩余的最后一个节点
- 只有我的"链接列表"中的第一个节点正在打印
- 避免在使用链接列表从 deque 中删除最后一个节点时出现内存泄漏
- C++ 分段错误:11 错误,同时编码将两个数字相加的链接列表
- 如何在C / C++中正确实现链接列表而不会使程序崩溃
- 将元素插入链接列表