如何将此旧的C 双重链接列表代码更新为C 11

How do I update this old C++ doubly linked list code to C++11?

本文关键字:列表 链接 代码 更新      更新时间:2023-10-16

我在现在称为 C++98

中编写的代码以下代码

它实现了双重链接列表,我正在考虑将其更新为C++11,但是我有以下问题:

  1. 似乎使用std::unique_ptr是有道理的,但是仍然需要其他指示"共享" unique_ptr。即,如果 nxt是唯一的_ptr,我该如何处理prviterator::it
  2. 似乎应该将分配器作为模板副手,但它将是T类型的分配者,而不是llnode?
  3. 是否有任何特征,需要"注册"?
  4. 如果使用move语义,则应定义哪些方法,例如 operator=() &&等?
  5. 需要更正什么不确定的行为(如果有)?
  6. 我不担心自己应该是什么?

一个公认的答案将简要讨论问题,然后在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)在迭代器之后插入数据,当标准是在迭代器之前插入时。您的方式使得不可能在开始时添加数据,并可以在末期之后添加数据。

  • 您的迭代器具有,但不需要removeinsert功能。我不会提到它,但是在这种情况下,他们要求迭代器的两倍。我会提供暂定的建议来删除它们,但只是试探性的。这是一个很小的罚款,也可能是一个有用的功能。

  • 您的operator =汇总所有内容,然后重新关注。在可能的情况下简单地复制元素可能会很方便。

  • 编码更棘手
  • 您缺少从一对迭代器构造的构造函数。

  • 您的迭代器允许从const容器中突变数据。这个是大

  • get_firstget_last通常称为frontback

我不担心自己应该是什么?

每个人都俯瞰的大型是例外。您的代码的例外安全,但这似乎是因为您的代码非常简单,并且您跳过了所有"硬"部分: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_allocmy_traits并且它们相同。
  • 从分配器私下继承。这有点奇怪,但通常可以节省8个字节。