stl容器详解

迭代器

简介

在 C++ 中,迭代器 是一种用于遍历容器(如 std::vector、std::list 等)元素的对象。它们提供了类似指针的接口,使得算法可以独立于具体的容器而工作。迭代器的设计允许算法以统一的方式处理不同类型的容器。

类别

为了使不同类型的迭代器能够支持不同的操作,C++ 标准库将迭代器分为以下几种类别,每种类别支持的操作能力逐级增强:

  1. 输入迭代器(Input Iterator)
  2. 输出迭代器(Output Iterator)
  3. 前向迭代器(Forward Iterator)
  4. 双向迭代器(Bidirectional Iterator)
  5. 随机访问迭代器(Random Access Iterator)
  6. 无效迭代器(Contiguous Iterator)(C++20 引入)
    每个类别都继承自前一个类别,具备更强的功能。例如,双向迭代器不仅支持前向迭代器的所有操作,还支持反向迭代(即可以向后移动)。
项目ValueValue
输入迭代器只读访问、单向前进单向链表 std::forward_list
输出迭代器只写访问、单向前进输出流 std::ostream_iterator
前向迭代器读写访问、单向前进向量 std::vector
双向迭代器读写访问、单向前进和反向迭代双向链表 std::list
随机访问迭代器读写访问、单向前进、反向迭代、跳跃移动(支持算术运算)向量 std::vector、队列 std::deque
无效迭代器(新)随机访问迭代器的所有功能,且元素在内存中连续排列新的 C++ 容器如 std::span

iterator_category 的作用

iterator_category 是迭代器类型中的一个别名,用于标识该迭代器所属的类别。它是标准库中 迭代器特性(Iterator Traits) 的一部分,标准算法会根据迭代器的类别优化其行为。

为什么需要iterator_category
标准库中的算法(如 std::sort、std::find 等)需要知道迭代器支持哪些操作,以便选择最优的实现方式。例如:
● 对于随机访问迭代器,可以使用快速的随机访问算法(如快速排序)。
● 对于双向迭代器,只能使用适用于双向迭代的算法(如归并排序)。
● 对于输入迭代器,只能进行单次遍历,许多复杂算法无法使用。
通过指定 iterator_category,你可以让标准算法了解你自定义迭代器的能力,从而选择合适的方法进行操作。

iterator_category 的声明

在你的自定义迭代器类中,通过以下方式声明迭代器类别:

using iterator_category = std::bidirectional_iterator_tag;

这表示该迭代器是一个 双向迭代器,支持向前和向后遍历。

std::bidirectional_iterator_tag 详解

std::bidirectional_iterator_tag 是一个标签(Tag),用于标识迭代器类别。C++ 标准库中有多个这样的标签,分别对应不同的迭代器类别:
● std::input_iterator_tag
● std::output_iterator_tag
● std::forward_iterator_tag
● std::bidirectional_iterator_tag
● std::random_access_iterator_tag
● std::contiguous_iterator_tag
这些标签本质上是空的结构体,用于类型区分。在标准算法中,通常会通过这些标签进行 重载选择(Overload Resolution) 或 特化(Specialization),以实现针对不同迭代器类别的优化。

● std::forward_iterator_tag 继承自 std::input_iterator_tag
std::bidirectional_iterator_tag 继承自 std::forward_iterator_tag
std::random_access_iterator_tag 继承自 std::bidirectional_iterator_tag
std::contiguous_iterator_tag 继承自 std::random_access_iterator_tag

迭代器特性(Iterator Traits)详解

C++ 提供了 迭代器特性(Iterator Traits),通过模板类 std::iterator_traits 来获取迭代器的相关信息。通过这些特性,标准算法可以泛化地处理不同类型的迭代器。

迭代器特性包含的信息
std::iterator_traits 提供以下信息:
iterator_category:迭代器类别标签。
value_type:迭代器指向的元素类型。
difference_type:迭代器间的距离类型(通常是 std::ptr diff_t)。
pointer:指向元素的指针类型。
reference:对元素的引用类型。

自定义迭代器与 iterator_traits

template<typename T>
class Iterator {
public:
    using iterator_category = std::bidirectional_iterator_tag;
    using value_type        = T;
    using difference_type   = std::ptrdiff_t;
    using pointer           = T*;
    using reference         = T&;

    // 其他成员函数...
};

这样,使用 std::iterator_traits<Iterator<T>> 时,就能正确获取迭代器的特性。

vector(动态数组)

vector是STL中最常用的序列容器之一,提供了动态大小的数组功能。它支持随机访问,允许在末尾高效地添加和删除元素。

定义与初始化

头文件#include <vector>
默认初始化std::vector<int> vec1;
指定大小和默认值std::vector<int>vec2(5,10);如果什么都不写等价于默认初始化
使用初始化列表std::vector<int> vec3 = {1,2,3,4,5};
拷贝构造 std::vector<int>vec4(vec3);
移动构造std::vector<int>vec5(std::move(vec4));

向量的基本操作

push_back()在向量末尾添加一个元素
pop_back()移除向量末尾的元素
insert()在指定位置插入元素
erase()移除指定位置的元素或范围内的元素
clear()移除所有元素

访问元素

operator[]通过索引访问数据
at()通过索引访问元素
front()访问第一个元素
back()访问最后一个元素

遍历

使用for循环
使用传统for循环
使用迭代器

向量的高级用法

嵌套向量(二维向量)

示例

#include <iostream>
#include <vector>

int main() {
	std::vector<std::vector<int>>matrix(3, std::vector<int>(4, 0));
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			matrix[i][j] = i * 4 + j + 1;
		}
	}
	std::cout << "Matrix:" << std::endl;
	for (auto row : matrix) {
		for (auto elem : row)
		{
			std::cout << elem << "\t";
		}
		std::cout<<std::endl;
	}
	return 0;
}

std::vector<std::vector<int>>matrix(3, std::vector<int>(4, 0));这一行代码和这个做对比std::vector<int>vec2(5,10);就能明显看出int类型换成vector类型后一个参数也随之转变

向量与其他数据结构结合

向量与结构体结合

#include <iostream>
#include <vector>
#include<string>

struct Student {
	int id;
	std::string name;
	float grade;
};

int main() {

	std::vector<Student> students;
	students.push_back({1001, "lls", 88.0});
	students.push_back({ 1001, "lls", 88.0 });
	students.push_back({ 1001, "lls", 88.0 });
	for (auto const& student : students) {
		std::cout << "ID" << student.id << "name" << student.name << "score" << student.grade << std::endl;
	}
	return 0;
}

使用迭代器操作向量

#include <iostream>
#include <vector>
int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    // 使用迭代器遍历并将每个元素值加10
    for (auto it = numbers.begin(); it != numbers.end(); ++it) {
        *it += 10;  // 修改元素,解引用迭代器
    }
    // 打印修改后的结果
    for (const auto& num : numbers) {
        std::cout << num << " ";  // 输出:11 12 13 14 15
    }
    std::cout << std::endl;
    return 0;
}

向量的性能与优化

内存管理

向量会动态的管理内存,自动调整其容量以适应新登或删除元素,频繁内存分配可能会影响性能。

预留空间

使用reserve()可以提前为向量分配足够的内存,减少内存重新分配的从实际,提高性能

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;

    // 预先为向量分配100个元素位置的内存
    v.reserve(100);

    std::cout << "After reserve(100): capacity = " << v.capacity() << std::endl;

    // 插入100个元素
    for (int i = 0; i < 100; ++i) {
        v.push_back(i);
    }

    std::cout << "After inserting 100 elements: size = " << v.size()
              << ", capacity = " << v.capacity() << std::endl;

    return 0;
}

收缩容量

使用shrink_to_fit()

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;
    
    // 预留较大的空间
    vec.reserve(1000);
    std::cout << "Capacity before adding: " << vec.capacity() << std::endl;
    
    // 添加少量元素
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    std::cout << "Size after adding: " << vec.size() << std::endl;
    std::cout << "Capacity after adding: " << vec.capacity() << std::endl;
    
    // 收缩容量
    vec.shrink_to_fit();
    std::cout << "Capacity after shrink_to_fit: " << vec.capacity() << std::endl;
    
    return 0;
} 

项目实战1学生信息管理系统

创建一个程序,管理学生的信息,包括添加、删除、显示和查找学生。每个学生包含ID、姓名和成绩。
1从实际需求设计数据结构
首先学生包含学号,姓名,成绩,所以用什么数据结构存储呢->结构体存储

struct Student {
	int id;
	std::string name;
	float grade;
};

选用合适的容器类型,由于学生管理系统需要经常的增删查改,所以选择vector容器std::vector<Student>
核心功能实现:
1打印学生信息

//1打印学生信息
void printStudent(const Student& student) {
	std::cout << "ID:" << student.id << "姓名:" << student.name << "成绩:" << student.grade << std::endl;
}

添加学生信息

//2添加学生信息
void addstudent(std::vector<Student> students) {
	Student s;
	std::cout << "请输入学生的id" ;
	std::cin >> s.id;
	std::cout << "请输入学生的姓名" ;
	std::cin.ignore();
	std::getline(std::cin, s.name);
	std::cout << "请输入学生的成绩" ;
	std::cin >> s.grade;
	students.push_back(s);
	std::cout << "输入成功" ;
}

std::cin.ignore的作用是忽略输入id时系统自带的换行符[enter],这个换行符会导致输入姓名是直接读取换行符导致读取姓名失败
std::getline 是用来从输入流(比如 std::cin)读取一整行字符,直到遇到换行符为止(换行符本身不包含在读取的字符串中)。
他可以读取一整行包含空格的文本(相比 std::cin >> s.name;
3删除学生

void deletstudent(std::vector<Student> students)
{
	int id;
	std::cout << "请输入删除学生的id";
	std::cin >> id;
	auto it = std::find_if(students.begin(), students.end(), [id](const Student& s) {return s.id == id; });
	if (it != students.end()) {
		students.erase(it);
		std::cout << "该学生被成功删除";
	}
	else {
		std::cout << "学生id" << id << "未能被找到" << std::endl;
	}
}

auto it = std::find_if(students.begin(), students.end(), [id](const Student& s) {return s.id == id; });核心代码先去找id,用find_if去找
Lambda表达式 [id](const Student& s) {return s.id == id; }[捕获对象](传入参数){执行条件}
4显示所有学生

void displayStudents(const std::vector<Student>& students) {
	if (students.empty()) {
		std::cout << "No students available.\n";
		return;
	}
	std::cout << "Student List:\n";
	for (const auto& s : students) {
		printStudent(s);
	}
}

5查找学生

void findStudent(const std::vector<Student>& students) {
	int id;
	std::cout << "Enter Student ID to find: ";
	std::cin >> id;
	auto it = std::find_if(students.begin(), students.end(), [id](const Student& s) {return s, id == id; });
	if (it != students.end()) {
		std::cout << "学生已找到";
		printStudent(*it);//it是一个迭代器,本身雷士指针指向某个元素在容器中的位置,解引用之后就是找到了学生结构体的对象
	}
	else {
		std::cout << "Student with ID " << id << " not found.\n";
	}

}

6.交互主函数

int main() {
	std::vector<Student> students;  // 存储学生列表
	int choice;

	do {
		// 菜单显示
		std::cout << "\n=== Student Management System ===\n";
		std::cout << "1. Add Student\n";
		std::cout << "2. Delete Student\n";
		std::cout << "3. Display All Students\n";
		std::cout << "4. Find Student by ID\n";
		std::cout << "5. Exit\n";
		std::cout << "Enter your choice (1-5): ";
		std::cin >> choice;

		switch (choice) {
		case 1:
			addstudent(students);
			break;
		case 2:
			deletstudent(students);
			break;
		case 3:
			displayStudents(students);
			break;
		case 4:
			findStudent(students);
			break;
		case 5:
			std::cout << "Exiting the system.\n";
			break;
		default:
			std::cout << "Invalid choice. Please choose between 1-5.\n";
		}
	} while (choice != 5);

	return 0;
}

完整代码:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

// 定义学生结构体
struct Student {
    int id;
    std::string name;
    float grade;
};

// 打印学生信息
void printStudent(const Student& student) {
    std::cout << "ID: " << student.id 
              << ", Name: " << student.name 
              << ", Grade: " << student.grade << std::endl;
}

// 添加学生
void addStudent(std::vector<Student>& students) {
    Student s;
    std::cout << "Enter Student ID: ";
    std::cin >> s.id;
    std::cout << "Enter Student Name: ";
    std::cin.ignore(); // 忽略之前输入的换行符
    std::getline(std::cin, s.name);
    std::cout << "Enter Student Grade: ";
    std::cin >> s.grade;
    students.push_back(s);
    std::cout << "Student added successfully.\n";
}

// 删除学生
void deleteStudent(std::vector<Student>& students) {
    int id;
    std::cout << "Enter Student ID to delete: ";
    std::cin >> id;
    
    auto it = std::find_if(students.begin(), students.end(), [id](const Student& s) {
        return s.id == id;
    });
    
    if(it != students.end()) {
        students.erase(it);
        std::cout << "Student deleted successfully.\n";
    }
    else {
        std::cout << "Student with ID " << id << " not found.\n";
    }
}

// 显示所有学生
void displayStudents(const std::vector<Student>& students) {
    if(students.empty()) {
        std::cout << "No students available.\n";
        return;
    }
    std::cout << "Student List:\n";
    for(const auto& s : students) {
        printStudent(s);
    }
}

// 查找学生
void findStudent(const std::vector<Student>& students) {
    int id;
    std::cout << "Enter Student ID to find: ";
    std::cin >> id;
    
    auto it = std::find_if(students.begin(), students.end(), [id](const Student& s) {
        return s.id == id;
    });
    
    if(it != students.end()) {
        std::cout << "Student Found:\n";
        printStudent(*it);
    }
    else {
        std::cout << "Student with ID " << id << " not found.\n";
    }
}

int main() {
    std::vector<Student> students;
    int choice;
    
    do {
        std::cout << "\n=== Student Management System ===\n";
        std::cout << "1. Add Student\n";
        std::cout << "2. Delete Student\n";
        std::cout << "3. Display All Students\n";
        std::cout << "4. Find Student by ID\n";
        std::cout << "5. Exit\n";
        std::cout << "Enter your choice (1-5): ";
        std::cin >> choice;
        
        switch(choice) {
            case 1:
                addStudent(students);
                break;
            case 2:
                deleteStudent(students);
                break;
            case 3:
                displayStudents(students);
                break;
            case 4:
                findStudent(students);
                break;
            case 5:
                std::cout << "Exiting the system.\n";
                break;
            default:
                std::cout << "Invalid choice. Please choose between 1-5.\n";
        }
        
    } while(choice != 5);
    
    return 0;
}

list列表

定义:
list是一个实现了双向链表的数据结构适合在容器中间频繁插入和删除元素。与vector不同,list不支持随机访问,但在任何位置的插入和删除操作都是常数时间
内部原理
list在内部使用双向链表,每个元素包含指向前一个和后一个元素的指针。这使得在已知位置插入或删除元素时,无需移动其他元素,只需更新指针即可。
应用场景
● 需要在容器中间频繁插入或删除元素。
● 不需要进行随机访问。
● 对内存的局部性要求不高(链表元素在内存中不连续)。

实现一个简单的list

类设计
我们的 List 类将包含以下组件:

  1. 节点结构体(Node):表示链表的每个节点。
  2. 迭代器类(Iterator):允许用户遍历链表。
  3. List 类:管理链表的基本操作,如插入、删除和遍历。
    节点结构体
template<typename T>
struct Node {
    T data;
    Node* prev;
    Node* next;
//为了减少拷贝开销使用引用
    Node(const T& value = T()) : data(value), prev(nullptr), next(nullptr) {}
};

定义一个迭代器迭代器是什么?
迭代器(Iterator) 是一个封装了指向容器中元素指针的对象,它允许我们用统一的方式访问容器中的元素(比如数组、链表、哈希表)。它是 C++ STL(标准模板库)的核心概念,是实现泛型算法和容器解耦的桥梁。
iterator_category = std::bidirectional_iterator_tag这一 typedef 说明迭代器的“类别”,告诉标准库算法该迭代器支持哪些操作:
std::input_iterator_tag :只支持往前单方向遍历,只读。
std::forward_iterator_tag :往前遍历,支持多次读。
std::bidirectional_iterator_tag :支持向前和向后(++ 和 --)遍历。
std::random_access_iterator_tag :支持跳跃访问,比如数组迭代器。

template <typename T>
class List;
template <typename T>
class Iterator {
public:
    using self_type = Iterator<T>;  //给当前迭代器类型定义一个别名(self_type)
    using value_type = T;//迭代器所指元素类型。
    using reference = T&;//元素的引用类型,比如 T&。
    using pointer = T*;//元素的指针类型,比如 T*。
    using iterator_category = std::bidirectional_iterator_tag;//支持向前和向后(++ 和 --)遍历。
    using difference_type = std::ptrdiff_t;//迭代器减法的结果类型,就是两个指针相减返回的类型。
    Iterator(Node<T>* ptr = nullptr) : node_ptr(ptr) {}
  private:
    Node<T> node_ptr;
    //迭代器内部持有指向链表中节点的指针。每次迭代的操作,都会通过改变 node_ptr 指向来遍历链表元素

双端队列

deque(双端队列)是一种支持在两端高效插入和删除元素的序列容器。与vector相比,deque支持在前端和后端均以常数时间进行插入和删除操作。
应用场景
● 需要在容器两端频繁插入和删除元素。
● 需要随机访问元素。
● 不需要频繁在中间位置插入和删除元素。
内存分配策略
deque内部并不使用一个单一的连续内存块,而是将元素分割成多个固定大小的块(也称为缓冲区或页面),并通过一个中央映射数组(通常称为map)来管理这些块。具体来说,deque的内部结构可以分为以下几个部分:

1. 中央映射数组(Map):
○ 一个指针数组,指向各个数据块。
○ map本身也是动态分配的,可以根据需要增长或收缩。
○ map允许deque在两端添加新的数据块,而无需移动现有的数据块。
2. 数据块(Blocks)
○ 每个数据块是一个固定大小的连续内存区域,用于存储元素。
○ 数据块的大小通常与编译器和平台相关,但在大多数实现中,数据块的大小在运行时是固定的(如512字节或更多,具体取决于元素类型的大小)。
3. 起始和结束指针:
○ deque维护指向中央映射数组中第一个有效数据块的指针以及第一个无效数据块的指针。
○ 这些指针帮助deque快速地在两端添加或删除数据块。
优点
● 双端操作高效:在两端插入和删除元素非常快速,不需要移动大量元素。
● 支持随机访问:可以像vector一样通过索引高效访问元素。
● 动态增长:无需预先定义大小,可以根据需要自动调整。
缺点
● 内存碎片:由于使用多个数据块,可能导致内存碎片,尤其是在大量插入和删除操作后。
● 较低的局部性:元素不连续存储,可能导致缓存未命中率较高,影响性能。
● 复杂性较高:内部实现相对复杂,不如vector直接高效。
代码实例

#include <iostream>
#include <deque>

int main() {
    // 创建一个空的deque
    std::deque<std::string> dq;

    // 在末尾添加元素
    dq.push_back("End1");
    dq.push_back("End2");

    // 在前端添加元素
    dq.push_front("Front1");
    dq.push_front("Front2");

    // 遍历deque
    std::cout << "deque中的元素: ";
    for(auto it = dq.begin(); it != dq.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 访问首尾元素
    std::cout << "首元素: " << dq.front() << std::endl;
    std::cout << "尾元素: " << dq.back() << std::endl;

    // 删除首元素
    dq.pop_front();

    // 删除尾元素
    dq.pop_back();

    // 打印删除后的deque
    std::cout << "删除首尾元素后: ";
    for(auto num : dq) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

手写双端队列

思路

  1. 动态数组: 使用动态数组(如环形缓冲区)来存储元素,以便支持在两端进行常数时间的插入和删除。
  2. 头尾指针: 维护头部和尾部的索引,以便快速访问两端。(头尾指针 )
  3. 自动扩展: 当容量不足时,自动调整内部缓冲区的大小。
  4. 迭代器支持: 定义一个迭代器类,允许用户使用像 begin() 和 end() 这样的函数进行遍历。

  1. 模板设计: 类模板适用于任意数据类型 T 的双端队列(Deque),能够存放整数、对象、指针等。
  2. 动态数组 (T buffer):* 用指针维护一块连续内存,容量动态变化。
  3. front_idx、back_idx: 这两个非常重要,是实现循环缓冲区的关键。底层内存是连续的,但逻辑上当插入到尾部超过数组大小后,索引会循环回0位置,从而实现“环形队列”功能。
  4. count和capacity: 计元素计数和缓冲区容量,容量变动时扩容。
template<typename T>
class Deque {
private:
    T* buffer;           // 存储元素的动态数组指针
    std::size_t capacity; // 当前缓冲区大小size_t表示无符号整数
    std::size_t count;    // 元素数量
    std::size_t front_idx; // 有效数据的第一个元素索引
    std::size_t back_idx;  // 下一个插入元素的后面位置索引

构造和析构函数
initial_capacity 初始容量

Deque(size_t initial_capacity = 10)
    : capacity(initial_capacity), count(0), front_idx(0), back_idx(0) {
    buffer = new T[capacity](); // 分配容量大小的内存,括号初始化为0或默认构造
}

~Deque() {
    delete[] buffer; // 释放动态分配内存,防止内存泄漏
}

基本属性访问
1.判断是否为空
2. size()返回元素数量,count的作用显而易见。

bool empty() const {
    return count == 0;
}
size_t size() const {
    return count;
}

扩容函数

void resize(size_t new_capacity) {
    T* new_buffer = new T[new_capacity]();
    for (size_t i = 0; i < count; ++i) {
        new_buffer[i] = buffer[(front_idx + i) % capacity]; // 逐个拷贝循环队列元素到新内存
    }
    delete[] buffer;
    buffer = new_buffer;
    front_idx = 0;
    back_idx = count;
    capacity = new_capacity;
}

插入

void push_front(const T& value) {
    if (count == capacity) {
        resize(capacity * 2);
    }
    // front_idx 向前移动一位,循环性质保证范围内
    front_idx = (front_idx == 0) ? capacity - 1 : front_idx - 1;
    buffer[front_idx] = value;
    ++count;
}
void push_back(const T& value) {
    if (count == capacity) {
        resize(capacity * 2);
    }
    buffer[back_idx] = value;
    back_idx = (back_idx + 1) % capacity;
    ++count;
}

删除

void pop_front() {
    if (empty()) {
        throw std::out_of_range("Deque is empty");
    }
    front_idx = (front_idx + 1) % capacity;
    --count;
}

void pop_back() {
    if (empty()) {
        throw std::out_of_range("Deque is empty");
    }
    back_idx = (back_idx == 0) ? capacity - 1 : back_idx - 1;
    --count;
}

取出元素

	T& back()
	{
		if (empty()) {
			throw std::out_of_range("Deque is empty");
		}
		size_t last_idx = back_idx == 0 ? capacity - 1 : back_idx - 1;
		return buffer[last_idx];
	}


	const T& back()const
	{
		if (empty()) {
			throw std::out_of_range("Deque is empty");
		}
		size_t last_idx = back_idx == 0 ? capacity - 1 : back_idx - 1;
		return buffer[last_idx];
	}
	T& front() {
		if (empty()) {
			throw std::out_of_range("Deque is empty");
		}
		return buffer[front_idx];
	}
	const T& front()const {
		if (empty()) {
			throw std::out_of_range("Deque is empty");
		}
		return buffer[front_idx];
	}

在实际中如果不关注底层实现,只关注逻辑上的作用,需要使用迭代器
迭代器的作用

  1. 复杂的环形索引细节包起来,用一个简单索引 pos 代表“第几个元素”
  2. 实现和常规指针类似的接口,让容器容易被for循环和算法无缝使用
  3. 负责容器中元素的访问和遍历
  4. 确保你不需要关注底层的物理存储结构,迭代器扛起了从逻辑到物理的映射责任
    迭代器定义的关键成员变量
Deque<T>* deque_ptr; // 指向你当前所在的 Deque 实例。迭代器需要问这个对象“我该去哪里访问元素?”
size_t pos;          // 逻辑上的位置,表示当前迭代器访问到第几个元素(0 ~ count)

迭代器常见的定义类型

using iterator_category = std::bidirectional_iterator_tag; // 表示这是一个双向迭代器,可以 ++ 和 --
using value_type = T;       // 迭代器跳出的元素类型
using difference_type = std::ptrdiff_t; // 迭代器之间的距离类型
using pointer = T*;         // 指针类型
using reference = T&;       // 引用类型,允许你修改元素

核心运算符重载
operator* 让你写 *it 来访问迭代器当前位置的元素。

reference operator*()const {
    size_t real_idx = (deque_ptr->front_idx + pos) % deque_ptr->capacity;
    return deque_ptr->buffer[real_idx];
}

operator* 让你写 *it 来访问迭代器当前位置的元素。

pointer operator->()const {
    size_t real_idx = (deque_ptr->front_idx + pos) % deque_ptr->capacity;
    return &(deque_ptr->buffer[real_idx]);
}

前置++ 和前置–

Iterator& operator++() { // ++it
    ++pos;
    return *this;
}

Iterator& operator--() { // --it
    --pos;
    return *this;
}

后置++和后置–

Iterator operator++(int) { // it++
    Iterator tmp = *this;
    ++pos;
    return tmp;
}

Iterator operator--(int) { // it--
    Iterator tmp = *this;
    --pos;
    return tmp;
}

比较运算符operator== 与 operator!=

bool operator==(const Iterator& other) const {
    return deque_ptr == other.deque_ptr && pos == other.pos;
}

bool operator!=(const Iterator& other) const {
    return !(*this == other);
}

begin和end

Iterator begin() {
    return Iterator(this, 0); // 迭代器刚开始指向第0个元素
}

Iterator end() {
    return Iterator(this, count); // 迭代器指向“无效”后面一个位置,用作循环终止
}

实例

Deque<int> d;
d.push_back(10);
d.push_back(20);
d.push_back(30);

for (auto it = d.begin(); it != d.end(); ++it) {
    std::cout << *it << "\n";  // 顺序打印 10 20 30
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值