C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++的list模拟实现

C++中的list模拟实现解读

作者:一枝小雨

文章详解C++ list模拟实现,采用双向循环链表结构,通过迭代器封装与运算符重载实现容器功能,涵盖构造、拷贝、内存管理及异常安全等关键点,并讨论了测试与改进方向,如移动语义和STL兼容性

list 模拟实现详解

1. 基本框架与节点结构

list的底层实现通常采用带头双向循环链表,每个节点包含指向前后节点的指针和数据域。

namespace practice_list
{
    template<class T>
    struct __list_node
    {
        __list_node<T>* _next;    // 指向下一个节点的指针
        __list_node<T>* _prev;    // 指向前一个节点的指针
        T _data;                  // 存储的数据

        // 节点构造函数,使用默认参数初始化
        __list_node(const T& x = T())
            : _data(x)           // 初始化数据域
            , _next(nullptr)     // 初始化next指针
            , _prev(nullptr)     // 初始化prev指针
        {}
    };
}

关键点说明:

2. 迭代器初步实现

迭代器是让链表能够像容器一样被遍历的关键,它封装了节点指针并重载了相关运算符。

2.1 简单迭代器实现

template<class T>
struct __list_iterator
{
    typedef __list_node<T> Node;
    Node* _node;  // 封装节点指针

    // 构造函数,通过节点指针初始化迭代器
    __list_iterator(Node* node)
        :_node(node)
    {}

    // 解引用操作符重载,返回节点数据的引用
    T& operator*()
    {
        return _node->_data;
    }

    // 前置++操作符重载,移动到下一个节点
    __list_iterator<T>& operator++()
    {
        _node = _node->_next;
        return *this;
    }

    // 不等于操作符重载,用于比较两个迭代器
    bool operator!=(const __list_iterator<T>& it)
    {
        return _node != it._node;
    }
};

关键点说明:

3. 基础list类实现

3.1 类定义与迭代器类型声明

template<class T>
class list
{
    typedef __list_node<T> Node;
public:
    // 声明迭代器类型
    typedef __list_iterator<T> iterator;
    
    // 获取指向第一个元素的迭代器
    iterator begin()
    {
        return iterator(_head->_next);  // 匿名对象写法
    }
    
    // 获取尾后迭代器(指向头节点)
    iterator end()
    {
        return iterator(_head);  // 匿名对象写法
    }
    
    // 构造函数,初始化带头双向循环链表
    list()
    {
        _head = new Node;      // 创建头节点
        _head->_next = _head;  // 头节点的next指向自己
        _head->_prev = _head;  // 头节点的prev指向自己
    }
    
    // 尾插函数
    void push_back(const T& x)
    {
        Node* tail = _head->_prev;  // 找到当前尾节点
        Node* newnode = new Node(x); // 创建新节点
        
        // 链接新节点与前一个节点(尾节点)
        tail->_next = newnode;
        newnode->_prev = tail;
        
        // 链接新节点与后一个节点(头节点)
        newnode->_next = _head;
        _head->_prev = newnode;
    }

private:
    Node* _head;  // 头节点指针
};

关键点说明:

3.2 测试函数

void test_list1()
{
    list<int> lt;
    lt.push_back(1);
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);

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

4. 完整迭代器实现

更完整的迭代器实现需要考虑const迭代器、前后移动等功能。

4.1 进阶迭代器设计

// 使用三个模板参数支持普通迭代器和const迭代器
// T: 数据类型, Ref: 引用类型, Ptr: 指针类型
template<class T, class Ref, class Ptr>
struct __list_iterator
{
    typedef __list_node<T> Node;
    typedef __list_iterator<T, Ref, Ptr> Self;  // 自身类型别名
    Node* _node;

    __list_iterator(Node* node)
        :_node(node)
    {}

    // 解引用操作符,返回Ref类型(T&或const T&)
    Ref operator*()
    {
        return _node->_data;
    }

    // 前置++
    Self& operator++()
    {
        _node = _node->_next;
        return *this;
    }

    // 后置++(int参数用于区分前置和后置)
    Self operator++(int)
    {
        Self ret = _node;  // 保存当前状态
        ++(*this);         // 调用前置++
        return ret;        // 返回之前保存的状态
    }

    // 前置--
    Self& operator--()
    {
        _node = _node->_prev;
        return *this;
    }

    // 后置--
    Self operator--(int)
    {
        Self ret = _node;
        --(*this);
        return ret;
    }

    // 不等于操作符
    bool operator!=(const Self& it)
    {
        return _node != it._node;
    }

    // 等于操作符
    bool operator==(const Self& it)
    {
        return !(_node != it._node);
    }

    // 箭头操作符,用于访问成员
    Ptr operator->()
    {
        return &(_node->_data);
    }
};

关键点说明:

5. 完整list类实现

5.1 类型定义与构造函数

template<class T>
class list
{
    typedef __list_node<T> Node;
private:
    Node* _head;  // 头节点指针
    
public:
    // 普通迭代器类型
    typedef __list_iterator<T, T&, T*> iterator;
    // const迭代器类型
    typedef __list_iterator<T, const T&, const T*> const_iterator;

    // 构造函数
    list()
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
    }
    
    // 析构函数
    ~list(){
        clear();     // 清空所有节点
        delete _head;        // 删除头节点
        _head = nullptr;
    }
    
    // 获取起始迭代器
    iterator begin()
    {
        return iterator(_head->_next);
    }
    
    // 获取尾后迭代器
    iterator end()
    {
        return iterator(_head);
    }
    
    // 获取const起始迭代器
    const_iterator begin() const
    {
        return const_iterator(_head->_next);
    }
    
    // 获取const尾后迭代器
    const_iterator end() const
    {
        return const_iterator(_head);
    }
    
    

5.2 拷贝构造与赋值重载

    // 拷贝构造函数
    list(const list<T>& lt)
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
        
        // 遍历源链表,插入数据
        const_iterator it = lt.begin();
        while (it != lt.end())
        {
            push_back(*it);
            ++it;
        }
        
        // 使用范围for,更简洁
        /*for (auto e : lt)
        {
                push_back(*it);
        }*/
    }
    
    /* 赋值重载 */
    /*list<T>& operator=(const list<T>& lt)
    {
            if (this != &lt)
            {
                    clear();
                    for (auto e : lt)
                    {
                            push_back(e);
                    }
            }
            else
                    return *this;
    }*/
    // 赋值运算符(简洁写法)
    list<T>& operator=(list<T> lt)  // 传参调用拷贝构造
    {
        swap(_head, lt._head);  // 交换头指针
        return *this;           // 返回当前对象
    }

5.3 元素访问与修改函数

    // 尾插
    void push_back(const T& x)
    {
        /*
        Node* tail = _head->_prev;
        Node* newnode = new Node(x);
        // 链接newnode与newnode前一个节点tail
        tail->_next = newnode;
        newnode->_prev = tail;
        // 链接newnode与newnod后一个节点"头节点"
        newnode->_next = _head;
        _head->_prev = newnode;
        */
        
        insert(end(), x);  // 在end()前插入
    }
    
    // 尾删
    void pop_back()
    {
        /*Node* tail = _head->_prev;
        Node* newtail = tail->_prev;
        delete[] tail;
        _head->_prev = newtail;
        newtail->_next = _head;*/

        erase(--end());
        // erase(iterator(_head->_prev)); 这种写法也可以
    }
    
    // 头插
    void push_front(const T& x)
    {
        insert(begin(), x);  // 在begin()前插入
    }
    
    // 头删
    void pop_front()
    {
        erase(begin());  // 删除第一个元素
    }
    
    // 在指定位置前插入
    void insert(iterator pos, const T& x)
    {
        Node* cur = pos._node;    // 当前节点
        Node* prev = cur->_prev;  // 前一个节点
        Node* newnode = new Node(x);  // 新节点
        
        // 链接新节点与前一个节点
        prev->_next = newnode;
        newnode->_prev = prev;
        
        // 链接新节点与当前节点
        newnode->_next = cur;
        cur->_prev = newnode;
    }
    
    // 删除指定位置元素
    void erase(iterator pos)
    {
        assert(pos != end());  // 不能删除头节点
        
        Node* cur = pos._node;
        Node* next = cur->_next;
        Node* prev = cur->_prev;
        
        delete cur;  // 释放节点内存
        
        // 重新链接前后节点
        next->_prev = prev;
        prev->_next = next;
    }
    
    // 清空链表(保留头节点)
    void clear()
    {
        iterator it = begin();
        while (it != end())
        {
        erase(it++);  // 使用后置++避免迭代器失效
        }
    }

};

6. 测试代码与特殊用例

6.1 基本功能测试

void test_list1()
{
    list<int> lt;
    lt.push_back(1);
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);

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

6.2 结构体访问测试

struct Date
{
    int _year = 0;
    int _month = 1;
    int _day = 1;
};

void test_list2()
{
    list<Date> lt;
    lt.push_back(Date());
    lt.push_back(Date());

    list<Date>::iterator it = lt.begin();
    while (it != lt.end())
    {
        cout << it->_year << "-" << it->_month << "-" << it->_day << endl;
        ++it;
    }
    cout << endl;
}

关键点说明:

6.3 const迭代器测试

void print_list(const list<int>& lt)
{
    list<int>::const_iterator it = lt.begin();
    while (it != lt.end())
    {
        // *it = 1;  // 错误:不能修改const引用指向的值
        cout << *it << " ";
        ++it;
    }
    cout << endl;
}

6.4 拷贝构造与赋值测试

void test_list4()
{
    list<int> lt;
    lt.push_back(1);
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);

    list<int> lt2(lt);  // 测试拷贝构造函数
    print_list(lt2);
}

void test_list5()
{
    list<int> lt;
    lt.push_back(1);
    lt.push_back(2);
    lt.push_back(3);
    lt.push_back(4);

    list<int> lt2;
    lt2.push_back(10);
    lt2.push_back(20);
    lt2.push_back(30);
    lt2.push_back(40);
    
    lt = lt2;  // 测试赋值运算符
    print_list(lt);
}

7. 补充说明与注意事项

7.1 迭代器设计的关键点

7.2 链表操作的关键点

7.3 可行的改进点

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文