c++标准模板库STL详解

1. 容器(Containers)

容器是用于存储和管理数据的对象,根据存储方式和数据结构的不同,可分为序列式容器、关联式容器和无序关联式容器。

序列式容器
  • 原理:元素在容器中按照线性顺序排列,每个元素都有固定的位置。
  • 常见容器
    • std::vector:基于动态数组实现,支持随机访问,在尾部插入和删除元素的时间复杂度为 O ( 1 ) O(1) O(1),在其他位置插入和删除元素的时间复杂度为 O ( n ) O(n) O(n)
    • std::list:基于双向链表实现,不支持随机访问,但在任意位置插入和删除元素的时间复杂度为 O ( 1 ) O(1) O(1)
    • std::deque:双端队列,结合了动态数组和链表的优点,支持在头部和尾部高效地插入和删除元素。
关联式容器
  • 原理:基于红黑树(一种自平衡二叉搜索树)实现,元素按照键的顺序排列,键是唯一的,通过键可以快速查找元素,查找、插入和删除操作的时间复杂度均为 O ( l o g n ) O(log n) O(logn)
  • 常见容器
    • std::map:存储键值对,键和值一一对应,按照键的升序排列。
    • std::set:只存储键,键是唯一的,按照键的升序排列。
无序关联式容器
  • 原理:基于哈希表实现,通过哈希函数将键映射到哈希表的某个位置,从而实现快速的查找、插入和删除操作,平均时间复杂度为 O ( 1 ) O(1) O(1),但在最坏情况下可能达到 O ( n ) O(n) O(n)
  • 常见容器
    • std::unordered_map:存储键值对,键是唯一的,元素无序。
    • std::unordered_set:只存储键,键是唯一的,元素无序。

2. 算法(Algorithms)

算法是一系列操作容器元素的通用函数,它们不依赖于具体的容器类型,只要容器提供了合适的迭代器,就可以使用这些算法。

原理

算法通过迭代器来访问容器中的元素,因此具有高度的通用性。STL 中的算法可以分为排序算法、查找算法、修改算法、比较算法等。

常见算法
  • std::sort:对指定范围内的元素进行排序,默认使用快速排序算法,平均时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)
  • std::find:在指定范围内查找第一个等于给定值的元素,返回指向该元素的迭代器。
  • std::copy:将一个范围的元素复制到另一个范围。

3. 迭代器(Iterators)

迭代器是一种抽象的指针,用于遍历容器中的元素,它提供了一种统一的方式来访问不同类型的容器。

原理

迭代器通过重载指针运算符(如 *->++-- 等)来实现对容器元素的访问和遍历,不同类型的容器可能提供不同类型的迭代器。

迭代器类型
  • 输入迭代器:只支持单向遍历和读取元素,如 std::find 算法使用的就是输入迭代器。
  • 输出迭代器:只支持单向遍历和写入元素。
  • 前向迭代器:支持单向遍历、读取和写入元素,是输入迭代器和输出迭代器的结合。
  • 双向迭代器:支持双向遍历、读取和写入元素,如 std::list 提供的迭代器就是双向迭代器。
  • 随机访问迭代器:支持随机访问、读取和写入元素,具有最高的灵活性,如 std::vector 提供的迭代器就是随机访问迭代器。

4. 函数对象(Function Objects)

函数对象也称为仿函数,是一种重载了函数调用运算符 () 的类或结构体的对象,它可以像函数一样被调用。

原理

函数对象可以封装一些操作或策略,并且可以携带状态,比普通函数更加灵活。STL 中的许多算法都可以接受函数对象作为参数,以实现不同的操作。

常见用途
  • 自定义排序规则:在 std::sort 算法中,可以传入一个函数对象来指定排序的比较规则。
#include 
#include 
#include 

struct Greater {
    bool operator()(int a, int b) const {
        return a > b;
    }
};

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5};
    std::sort(vec.begin(), vec.end(), Greater());
    for (int num : vec) {
        std::cout << num << " ";
    }
    return 0;
}

5. 适配器(Adapters)

适配器是一种特殊的容器或函数对象,它可以修改或扩展其他容器或函数对象的接口。

原理

适配器通过包装现有的组件,提供新的接口或功能,以满足特定的需求。

常见适配器
  • 容器适配器:如 std::stackstd::queuestd::priority_queue,它们分别基于 std::vectorstd::dequestd::list 实现,提供了栈、队列和优先队列的功能。
  • 函数适配器:如 std::bindstd::not1,可以用于绑定函数参数或取反函数的返回值。

6. 分配器(Allocators)

分配器用于管理容器中元素的内存分配和释放,它提供了一种抽象的内存管理机制,使得容器可以独立于具体的内存分配策略。

原理

分配器是一个模板类,定义了一系列用于内存分配和释放的函数,如 allocatedeallocate。STL 中的容器默认使用 std::allocator 作为分配器,但用户也可以自定义分配器。

自定义分配器示例
#include 
#include 
#include 

template <typename T>
class MyAllocator {
public:
    using value_type = T;

    MyAllocator() = default;

    template <typename U>
    MyAllocator(const MyAllocator<U>&) {}

    T* allocate(std::size_t n) {
        return static_cast<T*>(std::malloc(n * sizeof(T)));
    }

    void deallocate(T* p, std::size_t) {
        std::free(p);
    }
};

int main() {
    std::vector<int, MyAllocator<int>> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    for (int num : vec) {
        std::cout << num << " ";
    }
    return 0;
}

总结

STL 的各个组件相互协作,通过模板技术实现了高度的通用性和可复用性。容器提供了数据存储的方式,算法提供了对数据的操作,迭代器作为桥梁连接了容器和算法,函数对象、适配器和分配器则进一步扩展了 STL 的功能和灵活性。

你可能感兴趣的:(c++,开发语言)