C++ STL(标准模板库)中的容器是一组通用的、可复用的数据结构,用于存储和管理不同类型的数据。
目录
零. 简介:
一 . vector(动态数组)
二. list(双向链表)
三. deque(双端队列)
四. set(集合)
五. map(映射)
零. 简介:
STL 容器提供了一系列预定义的类,如 vector
(动态数组)、list
(双向链表)、deque
(双端队列)、set
(集合)、map
(映射)等。
意义和作用:
- 提高编程效率:无需自己实现常见的数据结构。
- 代码简洁:使用统一的接口和操作方式。
- 灵活性:适应不同的需求和数据类型。
- 可扩展性:易于添加新的容器或扩展现有容器的功能。
- 性能优化:经过精心设计和优化。
- 类型安全:在编译时进行类型检查。
- 代码可维护性:减少重复代码,提高代码的可读性和可维护性。
例如,使用 vector
可以方便地管理动态数组,而不需要自己手动实现动态内存分配和元素添加、删除等操作。
一 . vector
(动态数组)
vector
是 C++ STL 中的一种动态数组容器。它具有以下特点和优势:
- 动态调整大小:可以根据需要自动调整存储空间。
- 高效的随机访问:支持通过索引进行快速访问。
- 易于使用:提供了丰富的接口和操作方法。
- 自动内存管理:无需手动处理内存分配和释放。
有以下常用的 API:
push_back()
:在数组末尾添加元素。pop_back()
:删除数组末尾的元素。at()
:通过索引访问元素,提供边界检查。[]
操作符:通过索引访问元素。size()
:获取数组中元素的数量。empty()
:判断数组是否为空。begin()
:获取数组的起始迭代器。end()
:获取数组的末尾迭代器。clear()
:清空数组中的所有元素。insert()
:在指定位置插入元素。erase()
:删除指定元素或指定范围的元素。
以下是一个使用 vector
的示例代码:
#include <vector>
#include <iostream>
int main() {
std::vector<int> numbers;
// 向 vector 中添加元素
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
// 输出 vector 中的元素
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
前面的文章有更详细的介绍:http://t.csdnimg.cn/QMcDR
二. list
(双向链表)
list
是 C++ STL 中的双向链表容器.
它具有以下常用的 API:
push_back()
:在链表末尾添加元素。push_front()
:在链表开头添加元素。insert()
:在指定位置插入元素。erase()
:删除指定元素或指定范围的元素。clear()
:清空链表中的所有元素。size()
:获取链表中元素的数量。empty()
:判断链表是否为空。front()
:获取链表开头的元素。back()
:获取链表末尾的元素。begin()
:获取链表的起始迭代器。end()
:获取链表的末尾迭代器。
以下是一个使用 list
的示例代码:
#include <list>
#include <iostream>
int main() {
std::list<int> numbers;
// 在链表末尾添加元素
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
// 在链表开头添加元素
numbers.push_front(0);
// 输出链表中的元素
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
三. deque
(双端队列)
deque
是一种可以在两端高效地进行插入和删除操作的容器。
意义和作用:
- 高效的两端操作:可以在队列的两端快速添加和删除元素。
- 动态调整大小:自动根据需要调整内存。
- 支持随机访问:像数组一样,可以通过索引访问元素。
- 通用数据结构:适用于多种场景。
- 提高代码效率和可维护性:提供了简洁、高效的接口。
例如,可以使用 deque
来实现一个队列或栈的功能。
常用 API 包括:
push_back()
:在队列尾部添加元素。push_front()
:在队列头部添加元素。pop_back()
:从队列尾部删除元素。pop_front()
:从队列头部删除元素。insert()
:在指定位置插入元素。erase()
:删除指定元素或指定范围的元素。clear()
:清空队列中的所有元素。size()
:获取队列中元素的数量。empty()
:判断队列是否为空。begin()
:获取队列的起始迭代器。end()
:获取队列的末尾迭代器。
下面是一个简单的 deque
使用示例代码:
#include <deque>
#include <iostream>
int main() {
std::deque<int> numbers;
numbers.push_back(1); //向最后面插入数据 1
numbers.push_front(2);//向最钱面插入数据 2, 1
numbers.push_back(3); //向最后面插入数据 2, 1,3
numbers.push_front(4);//向最钱面插入数据 4, 2, 1,3
std::cout << "Front: " << numbers.front() << std::endl;
std::cout << "Back: " << numbers.back() << std::endl;
return 0;
}
四. set
(集合)
set
是一种无序且不允许重复元素的容器。
意义和作用:
- 自动去重:无需手动处理重复元素。
- 快速查找:提供高效的元素查找操作。
- 无序存储:元素的顺序是随机的。
- 键值对操作:常用于存储键值。
常用的 API 包括:
insert()
:插入元素。find()
:查找元素。erase()
:删除元素。size()
:获取元素数量。empty()
:判断是否为空。
下面是一个简单的示例代码:
#include <set>
#include <iostream>
int main() {
std::set<int> numbers = { 1, 2, 3, 4, 5 };
numbers.insert(3);//插入已有数据 3
numbers.insert(6); //插入未有数据
//循环打印
for (std::set<int>::iterator i = numbers.begin(); i != numbers.end(); ++i)
{
std::cout << *i << std::endl;
}
// 查找元素
if (numbers.find(3) != numbers.end()) {
std::cout << "Element found" << std::endl;
}
else {
std::cout << "Element not found" << std::endl;
}
return 0;
}
五. map
(映射)
map
(映射)是一种关联容器,它将键(key)和值(value)进行关联。 是一种无序容器,其中键必须是唯一的。
意义和作用:
- 提供了一种键值对的存储方式。
- 快速的键值查找。
- 自动根据键进行排序。
常用 API:
insert()
:插入键值对。find()
:根据键查找对应的值。erase()
:删除键值对。size()
:获取映射中键值对的数量。empty()
:判断映射是否为空。
下面是一个简单的示例代码:
#include <map>
#include <iostream>
int main() {
std::map<std::string, int> grades;
grades["a"] = 1;
grades["b"] = 2;
grades["c"] = 3;
// 根据键查找值
int aliceGrade = grades["a"];
std::cout << "a: " << aliceGrade << std::endl;
return 0;
}
扩展:
可以用来做观察者模式;
观察者模式是一种设计模式,它定义了对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
在 C++ 中,可以使用观察者模式来实现 map
的变化通知。下面是一个简单的示例代码:
#include <iostream>
#include <map>
#include <string>
#include <vector>
class Observer {
public:
virtual void update() = 0;
};
class Subject {
private:
std::map<std::string, int> data;
std::vector<Observer*> observers;
public:
void addObserver(Observer* observer) {
observers.push_back(observer);
}
void removeObserver(Observer* observer) {
auto it = std::find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
}
}
void updateObservers() {
for (Observer* observer : observers) {
observer->update();
}
}
void setKeyValue(const std::string& key, int value) {
data[key] = value;
updateObservers();
}
int getValue(const std::string& key) {
return data[key];
}
};
class DisplayObserver1 : public Observer {
public:
void update() override {
std::cout << "Data changed1" << std::endl;
}
};
class DisplayObserver2 : public Observer {
public:
void update() override {
std::cout << "Data changed2" << std::endl;
}
};
int main() {
Subject subject;
DisplayObserver1 observer1;
subject.addObserver(&observer1);
DisplayObserver2 observer2;
subject.addObserver(&observer2);
subject.setKeyValue("key1", 10);
return 0;
}