【C++初阶9-list实现】封装——我主沉浮,何不能至?

这篇具有很好参考价值的文章主要介绍了【C++初阶9-list实现】封装——我主沉浮,何不能至?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用

翻阅文档即可。

是什么

带头双向循环链表。

实现

1. 类成员设计

template <class T>
struct list_node
{
    T _val;
    list_node* _next;
    list_node* _prev;
};

template <class T>
class list
{
private:
    list_node<T> _head;
};

2. 类方法设计

namespace bacon {

    template<class T>
    struct listNode {
        T _val;
        listNode *_next;
        listNode *_prev;

        listNode(const T &val)
                : _val(val),
                  _next(nullptr),
                  _prev(nullptr) {}

    };
  
    template<class T>
    class list {
    public:
        typedef listNode<T> node;
        typedef listIterator<T> iterator;
        typedef listConstIterator<T> constIterator;

        list() { initialize();}

        ~list() {}

        list(const list<T> &other) {}

        list &operator=(list<T> &other) {}

    public:
        iterator begin() {}

        iterator end() {}

        constIterator begin() const {}

        constIterator end() const {}

        void pushBack(const T &val) {}

        void popBack() {}

        void pushFront(const T &val) {}

        iterator insert(iterator posIterator, const T &val) {}

        iterator erase(iterator posIterator) {}

        iterator find(const T &val) {}

        bool empty() {}

        void swap(list<T> &other) {}

    private:
        void initialize() {}

    private:
        listNode<T> *_dummyHead;
    };
}

3. 方法核心逻辑

*数据结构初阶已经实现过,这里只是变成类和对象的版本

namespace bacon {

    template<class T>
    struct listNode {
        T _val;
        listNode *_next;
        listNode *_prev;

        listNode(const T &val)
                : _val(val),
                  _next(nullptr),
                  _prev(nullptr) {}

    };

    template<class T>
    class list {
    public:
        typedef listNode<T> node;
        typedef listIterator<T> iterator;
        typedef listConstIterator<T> constIterator;

        list() { initialize(); }

        ~list() {
            node *cur = _dummyHead;
            while (cur != _dummyHead) {
                node *temp = cur;
                cur = cur->_next;
                delete temp;
            }
            _dummyHead = nullptr;
        }

        list(const list<T> &other) {
            initialize();
            for (const auto &e: other) pushBack(e);
        }

        list &operator=(list<T> &other) {
            swap(other);
            return *this;
        }

    public:
        iterator begin() {}

        iterator end() {}

        constIterator begin() const {}

        constIterator end() const {}

        void pushBack(const T &val) {
            node *newNode = new node(val);
            node *tail = _dummyHead->_prev;

            newNode->_next = _dummyHead;
            _dummyHead->_prev = newNode;

            tail->_next = newNode;
            newNode->_prev = tail;
        }

        void popBack() {
            assert(!empty());

            node *tail = _dummyHead->_prev;
            node *tailPrev = tail->_prev;

            tailPrev->_next = _dummyHead;
            _dummyHead->_prev = tailPrev;

            delete tail;
        }

        void pushFront(const T &val) {
            insert(begin(), val);
        }

        iterator insert(iterator posIterator, const T &val) {}

        iterator erase(iterator posIterator) {}

        void print() {
            node *cur = _dummyHead->_next;
            while (cur != _dummyHead) {
                cout << cur->_val << " ";
                cur = cur->_next;
            }
            cout << endl;
        }

        iterator find(const T &val) {
            node *cur = _dummyHead->_next;
            while (cur != _dummyHead) {
                if (cur->_val == val) return iterator(cur);
                cur = cur->_next;
            }
            return end();
        }

        bool empty() { return _dummyHead->_next == _dummyHead; }

        void swap(list<T> &other) {
            std::swap(_dummyHead, other._dummyHead);
        }

    private:
        void initialize() {
            _dummyHead = new node(0);
            _dummyHead->_next = _dummyHead->_prev = _dummyHead;
        }

    private:
        listNode<T> *_dummyHead;
    };
}

list的实现,价值最大的不是这里,而是迭代器……

4. 迭代器(重点)

封装,我主沉浮,何不能至?

封装给了我们很灵活的操作空间。为什么这么说?

迭代器是一种类的内嵌类型,具有指针行为,如果用不了原生指针,那我自己封装一个,自己实现它的指针行为。

这种玩法也叫做迭代器模式,是设计模式的一种:封装底层,隐藏底层细节;统一访问方式,降低使用成本。

template<class T>
struct listIterator {
    typedef listNode<T> node;
    node *_pnode;

    listIterator(node *pnode)
            : _pnode(pnode) {
    }

    T &operator*() { return _pnode->_val; }

    T *operator->() { return _pnode; }

    listIterator operator++() {
        _pnode = _pnode->_next;
        return *this;
    }

    listIterator operator--() {
        _pnode = _pnode->_prev;
        return *this;
    }

    bool operator!=(const listIterator<T> &other) { return _pnode != other._pnode; }
};
iterator insert(iterator posIterator, const T &val) {
    node *posPrev = posIterator._pnode->_prev;
    node *pos = posIterator._pnode;
    node *newNode = new node(val);

    newNode->_next = pos;
    pos->_prev = newNode;

    posPrev->_next = newNode;
    newNode->_prev = posPrev;

    return iterator(newNode);
}

iterator erase(iterator posIterator) {
    assert(!empty() && posIterator != end());

    node *posPrev = posIterator._pnode->_prev;
    node *posNext = posIterator._pnode->_next;

    posPrev->_next = posNext;
    posNext->_prev = posPrev;

    delete posIterator._pnode;
    return iterator(posNext);
}

const迭代器(重点)

const迭代器的行为和普通迭代器唯一的区别就是,const迭代器不能对对象进行修改,即不能写;但是能读!const迭代器本身是可以被修改的,但是它指向的对象不能通过const迭代器修改。

有的朋友可能认为const迭代器是这样的:

list<int> lt;
const list<int>::iterator = lt.begin();

这种写法,const保护的是iterator本身,并不能保护iterator指向的对象。

放到内置类型看看:

const int *p1; //保护p1指向的空间
int *const p2; //保护p2本身

const迭代器的行为其实是类似p1,保护指向的空间,但自己无需保护。

const list<int>::iterator = lt.begin();这样的写法,其实是类似p2,不符合迭代器的行为。

那我们该怎么实现const迭代器?

其实只需要控制operator*()operator->()的返回值T&T*即可。

怎么控制?

可不可以重载operator*()operator->()

T &operator*() { return _pnode->_val; }
const T &operator*() const { return _pnode->_val; }

T *operator->() { return _pnode; }
const T *operator->() const { return _pnode; }

其实不行的,为什么呢?

如下的代码,要用什么来调用?需要一个这样的对象:const iterator cit;

const T &operator*() const { return _pnode->_val; }

但我们已经讨论过,这样的对象是不符合const迭代器的行为的。所以这种方案舍弃掉了。

那我们可以再给一个类来作为const迭代器:

template<class T>
struct listConstIterator {
    typedef listNode<T> node;
    const node *_pnode;

    listConstIterator(node *pnode)
            : _pnode(pnode) {
    }

    const T &operator*() { return _pnode->_val; }

    const T *operator->() { return _pnode; }

    listConstIterator operator++() {
        _pnode = _pnode->_next;
        return *this;
    }

    listConstIterator operator--() {
        _pnode = _pnode->_prev;
        return *this;
    }

    bool operator!=(const listConstIterator<T> &other) { return _pnode != other._pnode; }
};

但我们发现,普通迭代器和const迭代器的区别就那么一点,却多用了一倍的代码来实现,太冗余了。

大佬给出了另一种方案:通过模版参数把operator*()operator->()的返回值泛化,来者是普通对象,返回T&T*;是const对象,返回const T&const T*

来看看参考代码吧:

template<class T, class Ref, class Ptr>
struct listIterator {
    typedef listNode<T> node;
    typedef listIterator<T, T &, T *> Self;
    node *_pnode;

    listIterator(node *pnode)
            : _pnode(pnode) {
    }

    Ref operator*() { return _pnode->_val; }

    Ptr operator->() { return _pnode; }

    Self &operator++() {
        _pnode = _pnode->_next;
        return *this;
    }

    Self &operator--() {
        _pnode = _pnode->_prev;
        return *this;
    }

    bool operator!=(const Self &other) { return _pnode != other._pnode; }
};

迭代器区间的构造

template<class InputIterator>
list(InputIterator first, InputIterator last) {
    initialize();
    while (first != last) {
        pushBack(*first);
        ++first;
    }
}

测试

//
//  main.cpp
//  list
//
//  Created by Bacon on 2023/4/13.
//

#include <iostream>
using namespace std;
#include "list.h"

void t1() {
    bacon::list<int> lt;

    lt.pushBack(1);
    lt.pushBack(2);
    lt.pushBack(3);
    lt.pushBack(4);
    lt.print();

    lt.popBack();
    lt.print();
    lt.popBack();
    lt.print();
    lt.popBack();
    lt.print();
}

void t2() {
    bacon::list<int> lt;

    lt.pushBack(1);
    lt.pushBack(2);
    lt.pushBack(3);
    lt.pushBack(4);
    lt.print();

    bacon::list<int>::iterator it = lt.begin();
    while(it != lt.end()) {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    for(auto& e : lt) cout << e << " ";
    cout << endl;

}

void t3() {
    bacon::list<int> lt;

    lt.pushBack(1);
    lt.pushBack(2);
    lt.pushBack(3);
    lt.pushBack(4);
    lt.print();

    bacon::list<int>::iterator pos = lt.find(3);
    lt.insert(pos, 999);
    lt.insert(lt.begin(), 111);
    lt.insert(lt.end(), 333);
    lt.print();

    pos = lt.find(999);
    lt.erase(pos);
    pos = lt.find(111);
    lt.erase(pos);
    pos = lt.find(333);
    lt.erase(pos);
    lt.print();
}

void t4() {
    bacon::list<int> lt;
    lt.pushBack(1);
    lt.pushBack(2);
    lt.pushBack(3);
    lt.pushBack(4);
    lt.print();

    bacon::list<int> lt1(lt);
    lt1.print();

    bacon::list<int> lt2;
    lt2 = lt1;
    lt2.print();
}

int main(int argc, const char * argv[]) {
    t1();
    cout << "–––––––––––––––––––––––––––-" << endl;
    t2();
    cout << "–––––––––––––––––––––––––––-" << endl;
    t3();
    cout << "–––––––––––––––––––––––––––-" << endl;
    t4();
    return 0;
}
1 2 3 4 
1 2 3 4 
1 2 3 4 
–––––––––––––––––––––––––––-
1 2 3 4 
111 1 2 999 3 4 333 
1 2 3 4 
–––––––––––––––––––––––––––-
1 2 3 4 
1 2 3 4 
1 2 3 4

vector vs list

vector

优点:

  • 随机访问O(1)
  • 尾部操作O(1)
  • CPU依照局部性原理拿取一坨数据时,缓存命中率高

缺点:文章来源地址https://www.toymoban.com/news/detail-427075.html

  • 其他位置操作O(n)
  • 因连续存储,需要动态扩容,可能浪费空间

*局部性原理:计算机中,访问某个位置的数据,其周围的数据也很可能被访问到。

为什么扩2倍

合适。扩多了容易浪费,扩少了容易频繁,开销大。

list

优点:

  • 任意位置操作O(1)
  • 逻辑上顺序存储,但是物理上随机存储,所以不会浪费空间

缺点:

  • 不支持随机访问,访问是O(n)
  • 缓存命中率低

整体代码

//
//  list.h
//  list
//
//  Created by Bacon on 2023/4/13.
//

#ifndef list_h
#define list_h

#include <iostream>
#include <cassert>


namespace bacon {

    template<class T>
    struct listNode {
        T _val;
        listNode *_next;
        listNode *_prev;

        listNode(const T &val)
                : _val(val),
                  _next(nullptr),
                  _prev(nullptr) {}

    };

    template<class T, class Ref, class Ptr>
    struct listIterator {
        typedef listNode<T> node;
        typedef listIterator<T, T &, T *> Self;
        node *_pnode;

        listIterator(node *pnode)
                : _pnode(pnode) {
        }

        Ref operator*() { return _pnode->_val; }

        Ptr operator->() { return _pnode; }

        Self &operator++() {
            _pnode = _pnode->_next;
            return *this;
        }

        Self &operator--() {
            _pnode = _pnode->_prev;
            return *this;
        }

        bool operator!=(const Self &other) { return _pnode != other._pnode; }
    };

//    template<class T>
//    struct listConstIterator {
//        typedef listNode<T> node;
//        const node *_pnode;
//
//        listConstIterator(node *pnode)
//                : _pnode(pnode) {
//        }
//
//        const T &operator*() { return _pnode->_val; }
//
//        const T *operator->() { return _pnode; }
//
//        listConstIterator operator++() {
//            _pnode = _pnode->_next;
//            return *this;
//        }
//
//        listConstIterator operator--() {
//            _pnode = _pnode->_prev;
//            return *this;
//        }
//
//        bool operator!=(const listConstIterator<T> &other) { return _pnode != other._pnode; }
//    };

    template<class T>
    class list {
    public:
        typedef listNode<T> node;
        typedef listIterator<T, T &, T *> iterator;
        typedef listIterator<T, const T &, const T *> constIterator;

        list() { initialize(); }

        ~list() {
            node *cur = _dummyHead;
            while (cur != _dummyHead) {
                node *temp = cur;
                cur = cur->_next;
                delete temp;
            }
            _dummyHead = nullptr;
        }

        template<class InputIterator>
        list(InputIterator first, InputIterator last) {
            initialize();
            while (first != last) {
                pushBack(*first);
                ++first;
            }
        }

        list(list<T> &other) {
            initialize();
            list<T> tmp(other.begin(), other.end());
            swap(tmp);
        }

        list &operator=(list<T> &other) {
            swap(other);
            return *this;
        }

    public:
        iterator begin() { return iterator(_dummyHead->_next); }

        iterator end() { return iterator(_dummyHead); }

        constIterator begin() const { return constIterator(_dummyHead->_next); }

        constIterator end() const { return constIterator(_dummyHead); }

        void pushBack(const T &val) {
            node *newNode = new node(val);
            node *tail = _dummyHead->_prev;

            newNode->_next = _dummyHead;
            _dummyHead->_prev = newNode;

            tail->_next = newNode;
            newNode->_prev = tail;
        }

        void popBack() {
            assert(!empty());

            node *tail = _dummyHead->_prev;
            node *tailPrev = tail->_prev;

            tailPrev->_next = _dummyHead;
            _dummyHead->_prev = tailPrev;

            delete tail;
        }

        void pushFront(const T &val) {
            insert(begin(), val);
        }

        iterator insert(iterator posIterator, const T &val) {
            node *posPrev = posIterator._pnode->_prev;
            node *pos = posIterator._pnode;
            node *newNode = new node(val);

            newNode->_next = pos;
            pos->_prev = newNode;

            posPrev->_next = newNode;
            newNode->_prev = posPrev;

            return iterator(newNode);
        }

        iterator erase(iterator posIterator) {
            assert(!empty() && posIterator != end());

            node *posPrev = posIterator._pnode->_prev;
            node *posNext = posIterator._pnode->_next;

            posPrev->_next = posNext;
            posNext->_prev = posPrev;

            delete posIterator._pnode;
            return iterator(posNext);
        }

        void print() {
            node *cur = _dummyHead->_next;
            while (cur != _dummyHead) {
                cout << cur->_val << " ";
                cur = cur->_next;
            }
            cout << endl;
        }

        iterator find(const T &val) {
            node *cur = _dummyHead->_next;
            while (cur != _dummyHead) {
                if (cur->_val == val) return iterator(cur);
                cur = cur->_next;
            }
            return end();
        }

        bool empty() { return _dummyHead->_next == _dummyHead; }

        void swap(list<T> &other) {
            std::swap(_dummyHead, other._dummyHead);
        }

    private:
        void initialize() {
            _dummyHead = new node(0);
            _dummyHead->_next = _dummyHead->_prev = _dummyHead;
        }

    private:
        listNode<T> *_dummyHead;
    };
}


#endif /* list_h */

到了这里,关于【C++初阶9-list实现】封装——我主沉浮,何不能至?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++初阶】11. list的使用及模拟实现

    list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。 list与forward_list非常相似:最主要的不同在

    2024年02月12日
    浏览(42)
  • C++初阶之一篇文章教会你list(模拟实现)

    成员类型表 这个表中列出了C++标准库中list容器的一些成员类型定义。这些类型定义是为了使list能够与C++标准库的其他组件协同工作,并提供一些通用的标准接口。每个成员类型的用处: value_type : 这个成员类型代表list容器中存储的数据类型,即模板参数T的类型。 allocator_

    2024年02月12日
    浏览(40)
  • 【C++历练之路】list的重要接口||底层逻辑的三个封装以及模拟实现

    W...Y的主页 😊 代码仓库分享💕  🍔前言: 在C++的世界中,有一种数据结构,它不仅像一个神奇的瑰宝匣,还像一位能够在数据的海洋中航行的智慧舵手。这就是C++中的list,一个引人入胜的工具,它以一种优雅而强大的方式管理着数据的舞台。想象一下,你有一个能够轻松

    2024年02月04日
    浏览(42)
  • C++初阶(十四)list

    📘北尘_ :个人主页 🌎个人专栏 :《Linux操作系统》《经典算法试题 》《C++》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是双向链表结构,双向链表中每个

    2024年02月04日
    浏览(36)
  • C++初阶(十一) list

    list的文档介绍 1. list是可以在常数范围内在 任意位置进行插入和删除 的序列式容器,并且该容器 可以前后双向迭代。 2. list的 底层是双向链表结构 ,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。 3. list与forward_lis

    2024年02月20日
    浏览(39)
  • 【1++的C++初阶】之list

    👍作者主页:进击的1++ 🤩 专栏链接:【1++的C++初阶】 list是可以在常数范围内进行任意插入和删除的序列式容器。 list底层是前后循环链表,因此可以双向前后迭代。与其他序列式容器相比,list的最大缺陷是不支持任意位置的随机访问。并且list还需要一些额外的空间来保存

    2024年02月16日
    浏览(35)
  • 【C++初阶】list的常见使用操作

    👦个人主页:@Weraphael ✍🏻作者简介:目前学习C++和算法 ✈️专栏:C++航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨ 功能:将数据进行链式存储。 链表( list )是一种物理存储单元上 非连续的存储结构 ,

    2024年02月11日
    浏览(38)
  • 【C++初阶】STL详解(五)List的介绍与使用

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 1.list是一种可以在常数范围内在任意位置进行插

    2024年02月04日
    浏览(58)
  • C++初阶之一篇文章教会你list(理解和使用)

    在C++标准库中, std::list 是一个双向链表容器,用于存储一系列元素。与 std::vector 和 std::deque 等容器不同, std::list 使用链表的数据结构来组织元素,因此在某些操作上具有独特的优势和性能特点。以下是关于 std::list 的详细介绍: 双向链表结构: std::list 内部使用双向链表来

    2024年02月13日
    浏览(51)
  • C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针

    入门知识已经梳理完毕了,接下来就进入到面型对象的部分学习了 C语言典型的 面向过程 的,关注的是过程,分析出求解问题的步骤,通过函数调用 逐步解决 问题 C++是典型的基于 面向对象 的,关注的是对象,将一件事情 拆分成不同的对象 ,靠对象之间的交互完成。 将大

    2024年01月19日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包