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

本文详细介绍了如何使用C++实现一个带头双向循环链表,包括类模板的设计、节点结构、基本操作如插入、删除、遍历等。重点讨论了迭代器的封装,以及如何通过模版参数优化实现const迭代器,同时提供了测试用例展示链表功能。
摘要由CSDN通过智能技术生成

使用

翻阅文档即可。

是什么

带头双向循环链表。

实现

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依照局部性原理拿取一坨数据时,缓存命中率高

缺点:

  • 其他位置操作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 */
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周杰偷奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值