【C++】list模拟实现全解析

发布于:2025-09-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

list 模拟实现详解

1. 基本框架与节点结构

2. 迭代器初步实现

2.1 简单迭代器实现

3. 基础list类实现

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

3.2 测试函数

4. 完整迭代器实现

4.1 进阶迭代器设计

5. 完整list类实现

5.1 类型定义与构造函数

5.2 拷贝构造与赋值重载

5.3 元素访问与修改函数

6. 测试代码与特殊用例

6.1 基本功能测试

6.2 结构体访问测试

6.3 const迭代器测试

6.4 拷贝构造与赋值测试

7. 补充说明与注意事项

7.1 迭代器设计的关键点

7.2 链表操作的关键点

7.3 可行的改进点


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;
    }
};

关键点说明:

  • 迭代器本质上是对节点指针的封装

  • 通过重载运算符实现指针-like的行为

  • operator*返回数据引用,使我们可以通过迭代器修改数据

  • operator++让迭代器移动到下一个节点

  • operator!=用于比较迭代器是否指向相同节点

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;  // 头节点指针
};

关键点说明:

  • list类维护一个头节点指针_head

  • begin()返回第一个有效元素的迭代器(头节点的下一个节点)

  • end()返回尾后迭代器(指向头节点本身)

  • 构造函数初始化一个空链表,头节点指向自己

  • push_back()在链表尾部插入新节点

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);
    }
};

关键点说明:

  • 使用三个模板参数实现普通迭代器和const迭代器的统一实现

  • 添加了前后移动操作(++和--的前置和后置版本)

  • 实现了箭头操作符,用于直接访问成员变量

  • 使用Self类型别名简化代码编写

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;
}

关键点说明:

  • it->等价于it.operator->(),返回Date*

  • 编译器特殊处理了it->member,使其等价于(it.operator->())->member

  • 这种语法糖提高了代码的可读性

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 迭代器设计的关键点

  1. 封装指针行为:迭代器封装了节点指针,使其能够像指针一样使用

  2. 模板技巧:通过模板参数区分普通迭代器和const迭代器

  3. 前后置运算符:正确实现++和--的前置和后置版本

  4. 箭头运算符:特殊处理->操作符以直接访问成员

7.2 链表操作的关键点

  1. 边界处理:始终维护双向循环链表的完整性

  2. 异常安全:在修改链表结构前分配资源,确保异常安全

  3. 迭代器失效:插入和删除操作可能导致迭代器失效,需要特别注意

  4. 内存管理:确保所有动态分配的节点都被正确释放

7.3 可行的改进点

  1. 异常处理:可以添加异常处理机制增强鲁棒性

  2. 性能优化:可以考虑添加移动语义支持(C++11)

  3. 更多容器操作:实现如resize(), merge(), sort()等常用操作

  4. 迭代器萃取:添加迭代器特性支持,与STL算法更好地配合


网站公告

今日签到

点亮在社区的每一天
去签到