06 模板链表、栈、队列

06 模板链表、栈、队列


作者:abnb250/山中鹤鸣
博客:https://www.cnblogs.com/abnb250
交流群:1062464078
点击返回目录



前情提要

这一节手写三个容器模板:
List(带头尾指针的双向单链)、Stack(用 new T[] 的连续数组)、Queue(环形缓冲区),
都完整实现了拷贝/移动构造和赋值。
注释版集中讲容器类的强异常安全与移动语义:拷贝赋值先构造临时对象再 swap,而不是先 clear 再复制;
移动构造必须把源对象指针置空,否则源对象析构会二次释放;
Stack 拷贝构造的 for(std::size_t i; ...) 漏了 i=0,是 UB;Queue::back 的 (index - 1 + capacity) % capacity 里 +capacity 不能省,tail==0 时 size_t 下溢。



代码(加注释版)

注释解释了两种场景:

  1. 代码有反直觉痕迹,自然写法就在旁边。
  2. 代码看起来自然,但背后有隐形决策。

List.h

#ifndef LIST_H
#define LIST_H

#include <iostream>
#include <algorithm>
#include <string>
#include <stdexcept>
using std::string;

//int, double, char, bool, string

template<class T>
class List{
private:
    struct Node{
        T val;
        Node* next;
    };
    Node* head;
    Node* tail;
    size_t count;
public:
    explicit List() : head(nullptr), tail(nullptr), count(0) {}
    ~List() {
        clear();
    }

    List(const List& other);
    List& operator=(const List& other);   
    List(List&& other) noexcept;
    List& operator=(List&& other) noexcept;
    
    void push_back(const T& t);
    void push_front(const T& t);
    void pop_back();
    void pop_front();

    T& front();
    const T& front() const;
    T& back();
    const T& back() const;

    bool empty() const;
    int size() const;
    void clear();
};

template<typename T>
List<T>::List(const List& other) : head(nullptr), tail(nullptr), count(0) {
    for(Node*cur = other.head; cur; cur = cur->next){
        push_back(cur->val);
    }
}

template<typename T>
List<T>& List<T>::operator=(const List& other){
    if(this == &other){
        return *this;
    }
    // 不能先 clear 再逐个复制,拷贝中途抛异常时原链表已毁,强异常安全丢失
    List tmp(other);
    std::swap(head, tmp.head);
    std::swap(tail, tmp.tail);
    std::swap(count, tmp.count);
    return *this;
}

template<typename T>
List<T>::List(List&& other) noexcept
    : head(other.head), tail(other.tail), count(other.count){
    // 不能只复制指针不置空 other,other 析构会 delete 同一批节点,二次释放
    other.head = other.tail = nullptr;
    other.count = 0;
}

template<typename T>
List<T>& List<T>::operator=(List&& other) noexcept{
    if(this == &other){
        return *this;
    }
    clear();
    head = other.head;
    tail = other.tail; 
    count = other.count;
    other.head = nullptr;
    other.tail = nullptr;
    other.count = 0;
    return *this;
}

template<typename T>
void List<T>::push_back(const T& t) {
    Node* tmp = new Node{t, nullptr};
    if(count != 0){
        tail->next = tmp;
    }
    else{
        head = tmp;
    }
    tail = tmp;
    count++;
}

template<typename T>
void List<T>::push_front(const T& t) {
    Node* tmp = new Node{t, nullptr};
    if(count != 0){
        tmp->next = head;
    }
    else{
        tmp->next = nullptr;
        tail = tmp;
    }
    head = tmp;
    count++;
}


template<typename T>
void List<T>::pop_back() {
    if(count == 0){
        throw std::out_of_range("count == 0");
    }
    Node* cur = head;
    if(count > 1){
        while(cur->next != tail){
            cur = cur->next;
        }
        Node* tmp;
        tmp = tail;
        tail = cur;
        cur = tmp;
        tail->next = nullptr;
    }
    else{
        head = nullptr;
        tail = nullptr;
    }
    delete cur;
    count--;
}

template<typename T>
void List<T>::pop_front() {
    if(count == 0){
        throw std::out_of_range("count == 0");
    }
    Node* cur = head;
    if(count == 1){
        head = nullptr;
        tail = nullptr;
    }
    else{
        head = head->next;
    }
    delete cur;
    count--;
}

template<typename T>
T& List<T>::front() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return head->val;
}

template<typename T>

const T& List<T>::front() const {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return head->val;
}

template<typename T>
T& List<T>::back() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return tail->val;
}

template<typename T>
const T& List<T>::back() const{
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return tail->val;
}

template<typename T>
bool List<T>::empty() const  {
    return count == 0;
}

template<typename T>
int List<T>::size() const {
    return count;
}

template<typename T>
void List<T>::clear() {
    while(count != 0){
        pop_front();
    }
}

#endif

Stack.h

#ifndef STACK_H
#define STACK_H

#include <iostream>
#include <iomanip>
#include <stdexcept>

template<class T>
class Stack{
private:
    std::size_t capacity;
    std::size_t count;
    T* data;
public:
    Stack() : capacity(0), count(0), data(nullptr) {}
    ~Stack();

    Stack(const Stack& other);
    Stack& operator=(const Stack& other);
    Stack(Stack&& other) noexcept;
    Stack& operator=(Stack&& other) noexcept;

    void push(const T& v);
    void pop();
    T& top();
    const T& top() const;
    bool empty() const;
    std::size_t size() const;
    void clear() ;

};

template<typename T>
Stack<T>::~Stack() {
    clear();
}

template<typename T>
Stack<T>::Stack(const Stack& other) 
    : capacity(other.capacity), 
    count(other.count), 
    data(new T[other.capacity]){

    // 不能省略 i 的初始化,i 未初始化,i < count 是 UB
    for(std::size_t i; i < count; i++){
        data[i] = other.data[i];
    }
}

template<typename T>
Stack<T>& Stack<T>::operator=(const Stack& other) {
    if(this == &other){
        return *this;
    }
    // 不能先 clear 再复制,拷贝抛异常时原栈已毁,强异常安全丢失
    Stack tmp(other);
    std::swap(count, tmp.count);
    std::swap(data, tmp.data);
    std::swap(capacity, tmp.capacity);
    return *this;
}

template<typename T>
Stack<T>::Stack(Stack&& other) noexcept
    : capacity(other.capacity), count(other.count), data(other.data){
    // 不能只复制指针不置空 other,other 析构会 delete[] 同一块内存
    other.data = nullptr;
    other.count = 0;
    other.capacity = 0;
}

template<typename T>
Stack<T>& Stack<T>::operator=(Stack&& other) noexcept {
    if(this == &other){
        return *this;
    }
    clear();
    data = other.data;
    count = other.count;
    capacity = other.capacity;
    other.data = nullptr;
    other.count = 0;
    other.capacity = 0;
    return *this;
}

template<typename T>
void Stack<T>::push(const T& v) {
    if(count == capacity){
        std::size_t newcap = (capacity == 0 ? 10 : capacity*2);
        // 不能先 delete[] data 再 new,分配失败时旧数据已丢,强异常安全丢失
        T* tmp = new T[newcap];
        for(std::size_t i = 0; i < count; i++){
            tmp[i] = data[i];
        }
        delete[] data;
        data = tmp;
        capacity = newcap;
    }
    data[count] = v;
    count++;
    
}

template<typename T>
void Stack<T>::pop() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    else{
        // 不能 pop 时析构元素,push 用赋值写入,对象必须保持存活
        count--;
    }
}

template<typename T>
T& Stack<T>::top() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    else{
        return data[count-1];
    }
}

template<typename T>
const T& Stack<T>::top() const {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    else{
        return data[count-1];
    }
}

template<typename T>
bool Stack<T>::empty() const {
    return count == 0;
}

template<typename T>
std::size_t Stack<T>::size() const {
    return count;
}

template<typename T>
void Stack<T>::clear() {
    delete[] data;
    capacity = 0;
    count = 0;
    data = nullptr;
}

#endif

Queue.h

#ifndef QUEUE_H
#define QUEUE_H

#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>

template<class T>
class Queue{
private:
    std::size_t capacity;
    std::size_t count;
    std::size_t head;
    std::size_t tail;
    T* data;
public:
    Queue(): capacity(0), count(0), head(0), tail(0), data(nullptr) {}
    ~Queue();

    Queue(const Queue& other);
    Queue& operator=(const Queue& other);
    Queue(Queue&& other) noexcept;
    Queue& operator=(Queue&& other) noexcept;

    void push(const T& b);
    void pop();
    T& front();
    const T& front() const;
    T& back();
    const T& back() const;

    bool empty() const;
    std::size_t size() const;
    void clear();

};

template<typename T>
Queue<T>::~Queue() {
    clear();
}

template<typename T>
Queue<T>::Queue(const Queue& other)
    : capacity(other.capacity), 
    count(other.count), 
    head(other.head), 
    tail(other.tail), 
    data(other.data) {
        if(capacity > 0){
            data = new T[capacity];
            for(std::size_t i = 0; i < capacity; i++){
                data[i] = other.data[i];
            }
        }
    }

template<typename T>
Queue<T>& Queue<T>::operator=(const Queue& other) {
    if(this == &other){
        return *this;
    }
    // 不能先 clear 再复制,拷贝抛异常时原队列已毁,强异常安全丢失
    Queue tmp(other);
    std::swap(capacity, tmp.capacity);
    std::swap(count, tmp.count);
    std::swap(head, tmp.head);
    std::swap(tail, tmp.tail);
    std::swap(data, tmp.data);
    return *this;
}

template<typename T>
Queue<T>::Queue(Queue&& other) noexcept
    : capacity(other.capacity), 
    count(other.count), 
    head(other.head),
    tail(other.tail),
    data(other.data) {
        // 不能只复制指针不置空 other,other 析构会 delete[] 同一块内存
        other.capacity = 0;
        other.count = 0;
        other.head = 0;
        other.tail = 0;
        other.data = nullptr;        
}

template<typename T>
Queue<T>& Queue<T>::operator=(Queue&& other) noexcept {
    if(this == &other){
        return *this;
    }
    clear();
    std::swap(capacity, other.capacity);
    std::swap(count, other.count);
    std::swap(head, other.head);
    std::swap(tail, other.tail);
    std::swap(data, other.data);
    return *this;
}

template<typename T>
void Queue<T>::push(const T& b) {
    if(count == capacity){
        std::size_t newcap = (capacity == 0 ? 10 : capacity*2);
        // 不能先 delete[] data 再 new,分配失败时旧数据已丢,强异常安全丢失
        T* tmp = new T[newcap];
        for(std::size_t i = 0; i < count; i++){
            std::size_t k = (head + i) % capacity;
            tmp[i] = data[k];
        }
        std::swap(tmp, data);
        delete[] tmp;
        capacity = newcap;
        head = 0;
        tail = count;
    }
    data[tail] = b;
    tail = (tail + 1) % capacity;
    count++;
    return;
}

template<typename T>
void Queue<T>::pop() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    // 不能 pop 时析构元素,push 用赋值写入,对象必须保持存活
    head = (head + 1) % capacity;
    count--;
    return;
}

template<typename T>
T& Queue<T>::front() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return data[head];
}

template<typename T>
const T& Queue<T>::front() const {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    return data[head];
}

template<typename T>
T& Queue<T>::back() {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    std::size_t index = tail;
    // 不能省略 + capacity,tail 为 0 时 index-1 下溢,% capacity 结果错误
    index = (index - 1 + capacity) % capacity;
    return data[index]; 
}

template<typename T>
const T& Queue<T>::back() const {
    if(count == 0){
        throw std::out_of_range("empty");
    }
    std::size_t index = tail;
    index = (index - 1 + capacity) % capacity;
    return data[index]; 
}

template<typename T>
bool Queue<T>::empty() const {
    return count == 0;
}

template<typename T>
std::size_t Queue<T>::size() const {
    return count;
}

template<typename T>
void Queue<T>::clear() {
    capacity = 0;
    count = 0;
    head = 0;
    tail = 0;
    delete[] data;
    data = nullptr;
}

#endif








posted @ 2026-11-01 17:36  abnb250  阅读(3)  评论(0)    收藏  举报