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 下溢。
代码(加注释版)
注释解释了两种场景:
- 代码有反直觉痕迹,自然写法就在旁边。
- 代码看起来自然,但背后有隐形决策。
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
/*
我要这天再遮不住我眼,我要这地再埋不住我心。
交流群:1062464078
*/

浙公网安备 33010602011771号