c++STL教程

STL 分为多个组件,包括容器(Containers)、迭代器(Iterators)、算法(Algorithms)、函数对象(Function Objects)和适配器(Adapters)等。

使用 STL 的好处:

  • 代码复用:STL 提供了大量的通用数据结构和算法,可以减少重复编写代码的工作。
  • 性能优化:STL 中的算法和数据结构都经过了优化,以提供最佳的性能。
  • 泛型编程:使用模板,STL 支持泛型编程,使得算法和数据结构可以适用于任何数据类型。
  • 易于维护:STL 的设计使得代码更加模块化,易于阅读和维护。

一、容器

STL 中的容器可以分为三类:

1、序列容器:存储元素的序列,允许双向遍历。

  • std::vector:动态数组,支持快速随机访问。
  • std::deque:双端队列,支持快速插入和删除。
  • std::list:链表,支持快速插入和删除,但不支持随机访问。

2、关联容器:存储键值对,每个元素都有一个键(key)和一个值(value),并且通过键来组织元素。

  • std::set:集合,不允许重复元素。
  • std::multiset:多重集合,允许多个元素具有相同的键。
  • std::map:映射,每个键映射到一个值。
  • std::multimap:多重映射,允许多个键映射到相同的值。

3、无序容器(C++11 引入):哈希表,支持快速的查找、插入和删除。

  • std::unordered_set:无序集合。
  • std::unordered_multiset:无序多重集合。
  • std::unordered_map:无序映射。
  • std::unordered_multimap:无序多重映射。

vector:

// 创建一个向量存储 int
   vector<int> vec; 
   int i;
 
   // 显示 vec 的原始大小
   cout << "vector size = " << vec.size() << endl;
 
   // 推入 5 个值到向量中
   for(i = 0; i < 5; i++){
      vec.push_back(i);
   }
 
   // 显示 vec 扩展后的大小
   cout << "extended vector size = " << vec.size() << endl;
 
   // 访问向量中的 5 个值
   for(i = 0; i < 5; i++){
      cout << "value of vec [" << i << "] = " << vec[i] << endl;
   }
 
   // 使用迭代器 iterator 访问值
   vector<int>::iterator v = vec.begin();
   while( v != vec.end()) {
      cout << "value of v = " << *v << endl;
      v++;
  • push_back( ) 成员函数在向量的末尾插入值,如果有必要会扩展向量的大小。
  • size( ) 函数显示向量的大小。
  • begin( ) 函数返回一个指向向量开头的迭代器。
  • end( ) 函数返回一个指向向量末尾的迭代器。

list:

  • 包含头文件:#include <list>
  • 声明列表:std::list<T> mylist;,其中 T 是存储在列表中的元素类型。
  • 插入元素:mylist.push_back(value);
  • 删除元素:mylist.pop_back(); 或 mylist.erase(iterator);
  • 访问元素:mylist.front(); 和 mylist.back();
  • 遍历列表:使用迭代器 for (auto it = mylist.begin(); it != mylist.end(); ++it)

set:

语法

包含头文件:#include <set>

声明 set 容器std::set<元素类型> 容器名;

常用操作

  • insert(元素): 插入一个元素。
  • erase(元素): 删除一个元素。
  • find(元素): 查找一个元素。
  • size(): 返回容器中元素的数量。
  • empty(): 检查容器是否为空。

map:

定义和特性

  • 键值对map 存储的是键值对,其中每个键都是唯一的。
  • 排序map 中的元素按照键的顺序自动排序,通常是升序。
  • 唯一性:每个键在 map 中只能出现一次。
  • 双向迭代器map 提供了双向迭代器,可以向前和向后遍历元素。

基本语法

包含头文件:#include <map>

声明 map 容器:

std::map<key_type, value_type> myMap;
  • key_type 是键的类型。
  • value_type 是值的类型。

插入元素:myMap[key] = value;

访问元素:value = myMap[key];

遍历 map:

for (std::map<key_type, value_type>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
    std::cout << it->first << " => " << it->second << std::endl;
}

二、迭代器

迭代器(Iterator)是C++标准模板库(STL)中的一个重要概念,它提供了一种访问容器中元素的方法,而无需暴露容器的内部实现细节。迭代器可以被视为一种“智能指针”,它能够在容器内部进行遍历,同时提供对容器元素的访问。

定义

迭代器并不是一个具体的类型,而是一个抽象的概念。在C++中,迭代器通常是通过模板类实现的,这些模板类提供了对容器元素的访问和操作。迭代器必须至少支持*(解引用)、++(递增)、--(递减,如果支持的话)、==(相等性比较)和!=(不等性比较)等操作。

迭代器类型

根据迭代器的功能,C++ STL将迭代器分为以下五类(从低到高):

  1. 输入迭代器(Input Iterators):只能向前移动,并支持解引用和相等性比较操作。
  2. 输出迭代器(Output Iterators):只能向前移动,并支持赋值操作。
  3. 前向迭代器(Forward Iterators):是输入迭代器的超集,也支持多次通过迭代器访问同一元素。
  4. 双向迭代器(Bidirectional Iterators):是前向迭代器的超集,支持向前和向后移动。
  5. 随机访问迭代器(Random Access Iterators):是双向迭代器的超集,支持通过索引直接访问元素,以及进行迭代器间的算术操作。

使用方法

迭代器的基本使用方法包括获取迭代器、遍历容器、访问元素以及修改元素(如果迭代器支持)。

获取迭代器

容器通常提供成员函数来返回指向容器首元素和尾后元素(即最后一个元素的下一个位置,不指向任何有效元素)的迭代器。

  • begin():返回指向容器首元素的迭代器。
  • end():返回指向容器尾后元素的迭代器。
遍历容器

可以使用循环结构(如for循环、while循环)结合迭代器来遍历容器。

#include <vector>  
#include <iostream>  
  
int main() {  
    std::vector<int> vec = {1, 2, 3, 4, 5};  
    for (auto it = vec.begin(); it != vec.end(); ++it) {  
        std::cout << *it << " ";  
    }  
    std::cout << std::endl;  
  
    // 使用C++11范围for循环,更加简洁  
    for (int val : vec) {  
        std::cout << val << " ";  
    }  
    std::cout << std::endl;  
  
    return 0;  
}
访问和修改元素
  • 访问元素:使用解引用操作符*来访问迭代器指向的元素。
  • 修改元素:如果迭代器支持(通常是双向迭代器或随机访问迭代器),可以直接通过解引用和赋值来修改元素。
if (it != vec.end()) {  
    *it = 10; // 修改迭代器指向的元素  
}

注意事项

  • 迭代器失效:在某些容器操作中(如vectorpush_back可能会导致重新分配内存),迭代器可能会失效。在使用迭代器之前,需要确保它们仍然有效。
  • 迭代器比较:只能比较来自同一容器的迭代器。
  • 迭代器算术操作:只有随机访问迭代器支持算术操作,如it + nit - nit[n]等。

三、算法(Algorithms)

C++ 标准库中的 <algorithm> 头文件提供了一组用于操作容器(如数组、向量、列表等)的算法。这些算法包括排序、搜索、复制、比较等,它们是编写高效、可重用代码的重要工具。

<algorithm> 头文件定义了一组模板函数,这些函数可以应用于任何类型的容器,只要容器支持迭代器。这些算法通常接受两个或更多的迭代器作为参数,表示操作的起始和结束位置。

常用算法包括

  • sort:对一个序列进行排序。
  • find:在一个序列中查找指定元素。
  • count:统计一个序列中某个元素的个数。
  • max_element:找到一个序列中的最大元素。
  • min_element:找到一个序列中的最小元素。
  • accumulate:对一个序列中的元素进行累加。
  • reverse:将一个序列进行反转。
  • unique:将一个序列中的重复元素去重。
  • fill:将一个序列中的所有元素设置为指定值。
  • copy:将一个序列中的元素复制到另一个序列中。
  • transform:对一个序列中的元素进行转换后,存储到另一个序列中。
  • binary_search:在一个有序序列中查找指定元素。
  • next_permutation:生成一个序列的下一个排列。
  • prev_permutation:生成一个序列的上一个排列。

语法:

大多数 <algorithm> 中的函数都遵循以下基本语法:

algorithm_name(container.begin(), container.end(), ...);

这里的 container 是一个容器对象,begin() 和 end() 是容器的成员函数,返回指向容器开始和结束的迭代器。

1、sort排序

定义:对容器中的元素进行排序

语法:sort(container.begin(), container.end(), compare_function);

2. 搜索算法

函数:find

定义:在容器中查找与给定值匹配的第一个元素。

语法:

auto it = find(container.begin(), container.end(), value);

如果找到,it 将指向匹配的元素;如果没有找到,it 将等于 container.end()。

3. 复制算法

函数:copy

定义:将一个范围内的元素复制到另一个容器或数组。

语法:copy(source_begin, source_end, destination_begin);

4. 比较算法

函数:equal

定义:比较两个容器或两个范围内的元素是否相等。

语法:bool result = equal(first1, last1, first2);

或 bool result = equal(first1, last1, first2, compare_function);

四、函数对象

定义一个类需要使用关键字 class,然后指定类的名称,并类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数。

定义一个类,本质上是定义一个数据类型的蓝图,它定义了类的对象包括了什么,以及可以在这个对象上执行哪些操作。

特点与优势

  1. 参数与返回值:函数对象可以像普通函数一样有参数和返回值,这使得它们非常灵活。
  2. 状态保持:与普通函数不同,函数对象可以有自己的状态(例如,成员变量),这允许它们在多次调用之间保持状态。这种能力使得函数对象能够记录调用次数、累积结果等。
  3. 可重用性:由于函数对象是类的实例,因此可以轻松地创建和使用多个具有相同行为但不同状态的实例。
  4. 封装性:函数对象可以将数据和操作这些数据的函数封装在同一个类中,从而提高代码的模块化和可维护性。
  5. 灵活性:函数对象可以作为参数传递给其他函数,也可以作为返回值从函数中返回。这使得它们能够灵活地与其他函数和对象进行交互。

五、适配器

STL(Standard Template Library,标准模板库)中的适配器(Adapter)是一种设计模式,它允许将某个类的接口转换为客户端所期望的另一个接口,从而使原本因接口不兼容而不能一起工作的类可以一起工作。在STL中,适配器主要用于容器、迭代器和函数对象,分别称为容器适配器、迭代器适配器和函数适配器

1. 容器适配器

容器适配器是一种特殊的容器,它们通过封装另一个容器(如vectordequelist)来提供特定的接口。STL提供了三种容器适配器:stackqueuepriority_queue。这些适配器通过限制对底层容器的直接访问,只提供符合特定数据结构(如栈、队列或优先队列)操作的接口。

  • stack:提供后进先出(LIFO)的操作接口。
  • queue:提供先进先出(FIFO)的操作接口。
  • priority_queue:提供基于优先级的队列操作接口,元素总是按照特定的顺序(如最大堆或最小堆)被移除。

2. 迭代器适配器

迭代器适配器用于改变迭代器的行为,使它们表现得像另一种迭代器。STL提供了多种迭代器适配器,如insert_iteratorreverse_iteratoristream_iterator等。

  • insert_iterator:允许通过迭代器进行插入操作,而不是替换操作。
  • reverse_iterator:允许以逆序遍历容器。
  • istream_iterator:可以将输入流(如std::cin)转换为迭代器,用于读取输入流中的元素。

3. 函数适配器

函数适配器用于改变函数对象(也称为仿函数)的行为。它们可以接受一个或多个函数对象作为参数,并返回一个新的函数对象,该对象的行为是对原函数对象行为的某种修改或扩展。STL中的函数适配器包括std::bindstd::mem_fnstd::not1std::not2等。

  • std::bind:用于绑定函数对象的某些参数到具体的值,从而生成一个新的函数对象。
  • std::mem_fn:用于生成一个可调用对象,该对象可以像调用普通函数一样调用类的成员函数。
  • std::not1std::not2:用于对一元和二元函数对象的返回值进行逻辑否定。

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值