c++顺序容器

文章目录

  • c++ 顺序容器概述
  • 容器通用操作
      • 1. 构造和析构
        • 示例:`std::vector` 的构造
      • 2. 迭代器操作
        • 示例:使用迭代器遍历 `std::vector`
      • 3. 容量查询
        • 示例:检查 `std::vector` 的大小
      • 4. 元素访问
        • 示例:访问 `std::vector` 的元素
      • 5. 修改操作
        • 示例:修改 `std::vector`
      • 完整代码示例
  • 迭代器
      • 1. 迭代器的概念和类型
      • 2. 迭代器的操作
        • 示例:使用迭代器遍历 `std::vector`
      • 3. 迭代器的高级应用
        • 示例:使用迭代器进行插入和删除
      • 4. 注意事项
      • 完整代码示例
  • 容器定义和初始化
      • 示例代码
      • 解释
  • C++ 容器赋值和 Swap
      • 1. 赋值操作
        • 示例:`std::vector` 的赋值
      • 2. Swap 操作
        • 示例:使用 `swap` 交换 `std::vector` 的内容
      • 完整代码示例
  • 容器大小操作
      • 1. 检查是否为空
        • 示例:检查 `std::vector` 是否为空
      • 2. 获取大小
        • 示例:获取 `std::vector` 的大小
      • 3. 改变大小
        • 示例:改变 `std::vector` 的大小
      • 4. 添加和删除元素
        • 示例:在 `std::vector` 中添加和删除元素
      • 完整代码示例
  • 顺序容器操作
      • 1. 访问元素
        • 示例:访问 `std::vector` 中的元素
      • 2. 前后元素访问
        • 示例:访问 `std::vector` 的第一个和最后一个元素
      • 3. 迭代器
        • 示例:遍历 `std::vector`
      • 4. 修改容器
        • 示例:修改 `std::vector`
      • 5. 容器大小
        • 示例:检查 `std::vector` 的大小和状态
      • 完整代码示例
  • 特殊的 forward_list 操作
      • 1. 在指定位置前插入元素
        • 示例:在 `std::forward_list` 中插入元素
      • 2. 删除元素
        • 示例:删除 `std::forward_list` 中的元素
      • 3. 改变容器大小
      • 4. 添加和删除元素
        • 示例:在 `std::forward_list` 的开头添加和删除元素
      • 完整代码示例
  • 容器操作可能使迭代器失效
      • 1. 添加或删除元素
        • 示例:`std::vector` 添加元素导致迭代器失效
      • 2. `std::vector` 和 `std::string` 的动态内存重新分配
      • 3. `std::list` 和 `std::forward_list` 的元素删除
        • 示例:`std::list` 删除元素导致迭代器失效
      • 4. `std::map` 和 `std::set` 的元素删除
      • 防止迭代器失效的策略
      • 完整代码示例
  • Vector 对象的增长机制
      • 1. 动态内存分配
        • a. 计算新的容量
        • b. 分配新内存
        • c. 移动元素
        • d. 释放旧内存
      • 2. 复杂性和性能
      • 3. 保留额外容量
      • 示例
  • 额外的 string 操作
      • 1. 字符串连接
        • 示例:字符串连接
      • 2. 查找和替换
        • 示例:查找和替换
      • 3. 插入和删除
        • 示例:插入和删除
      • 4. 子字符串
        • 示例:提取子字符串
      • 5. 字符串比较
        • 示例:字符串比较
      • 完整代码示例
  • 容器适配器
    • 1. Stack(栈)
        • 示例:使用 `std::stack`
    • 2. Queue(队列)
        • 示例:使用 `std::queue`
    • 3. Priority Queue(优先队列)
        • 示例:使用 `std::priority_queue`

c++ 顺序容器概述

C++ 中的顺序容器是标准模板库(STL)的一部分,它提供了一组用于存储和管理对象序列的模板类。这些容器在内存中以线性顺序存储元素,允许快速顺序访问。顺序容器的主要类型包括:

  1. vector:动态数组,支持快速随机访问。在向量的末尾添加或删除元素比较高效,但在中间或开头插入或删除元素可能较慢。

  2. deque(双端队列):与 vector 类似,但设计用于在两端快速插入和删除元素。

  3. list:双向链表,支持在任何位置快速插入和删除元素,但不支持快速随机访问。

  4. forward_list:单向链表,与 list 类似,但仅支持向前遍历。相比 list,它使用更少的内存。

  5. array:固定大小的数组。它提供与内置数组类似的功能,但增加了一些额外的功能,如 STL 兼容的迭代器。

  6. string:专门用于字符的容器,提供了对字符串进行操作的丰富功能。

容器通用操作

在 C++ 中,标准模板库(STL)提供了多种容器,每种容器都支持一些通用操作。下面是这些操作的详细介绍

1. 构造和析构

构造:容器可以通过多种方式构造,如默认构造、复制构造、范围构造等。
析构:容器对象离开其作用域时,其析构函数会被自动调用,释放所有资源。

示例:std::vector 的构造
std::vector<int> vec1; // 默认构造
std::vector<int> vec2 = {1, 2, 3, 4, 5}; // 初始化列表构造
std::vector<int> vec3(vec2); // 复制构造

2. 迭代器操作

begin()end():返回指向容器第一个元素和尾后元素的迭代器。
rbegin()rend():返回反向迭代器。

示例:使用迭代器遍历 std::vector
for(auto it = vec2.begin(); it != vec2.end(); ++it) {
    std::cout << *it << " ";
}

3. 容量查询

empty():检查容器是否为空。
size():返回容器中元素的数目。

示例:检查 std::vector 的大小
std::cout << "Is empty: " << vec1.empty() << "\n";
std::cout << "Size: " << vec2.size() << "\n";

4. 元素访问

operator[]at():用于访问元素。
front()back():访问第一个和最后一个元素。

示例:访问 std::vector 的元素
std::cout << "First element: " << vec2.front() << "\n";
std::cout << "Last element: " << vec2.back() << "\n";

5. 修改操作

insert():在指定位置插入元素。
erase():删除一个或一范围的元素。
push_back()pop_back():在末尾添加和移除元素。

示例:修改 std::vector
vec2.push_back(6); // 在末尾添加元素
vec2.erase(vec2.begin()); // 移除第一个元素

完整代码示例

下面是一个综合示例,综合了上述所有操作。

#include 
#include 

int main() {
    // 使用初始化列表构造向量
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 输出原始向量
    std::cout << "Original vector: ";
    for(const auto& value : vec) {
        std::cout << value << " ";
    }
    std::cout << "\\n";

    // 检查向量是否为空并输出大小
    std::cout << "Is empty: " << vec.empty() << "\\n";
    std::cout << "Size: " << vec.size() << "\\n";
    
    // 输出第一个和最后一个元素
    std::cout << "First element: " << vec.front() << "\\n";
    std::cout << "Last element: " << vec.back() << "\\n";

    // 修改向量
    vec.push_back(6); // 在末尾添加元素
    vec.erase(vec.begin()); // 移除第一个元素

    // 输出修改后的向量
    std::cout << "Modified vector: ";
    for(const auto& value : vec) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

Original vector: 1 2 3 4 5 
Is empty: 0
Size: 5
First element: 1
Last element: 5
Modified vector: 2 3 4 5 6 
  • 原始向量:打印了初始向量中的元素(1 2 3 4 5)。
  • 容器状态查询
    • 向量非空(Is empty: 00 表示 false)。
    • 向量的初始大小为 5
  • 元素访问
    • 第一个元素是 1
    • 最后一个元素是 5
  • 修改向量
    • 向量末尾添加了一个元素 6
    • 从向量开头移除了第一个元素(原来的 1)。
  • 修改后的向量:最终,修改后的向量元素为 2 3 4 5 6

迭代器

1. 迭代器的概念和类型

概念:迭代器是一个允许程序员在容器(如数组或STL容器)上进行遍历的对象。它类似于指针,但提供了更高层次的抽象。

类型

  • 输入迭代器:仅用于从容器中读取数据。
  • 输出迭代器:仅用于向容器写入数据。
  • 前向迭代器:用于单向读写操作。
  • 双向迭代器:支持向前和向后遍历。
  • 随机访问迭代器:支持直接访问任意元素,类似数组。

2. 迭代器的操作

操作

  • 解引用:通过 *iterator 访问迭代器指向的元素。
  • 自增++iterator 移动到下一个元素。
  • 自减:对于双向迭代器,--iterator 移动到前一个元素。
  • 比较:使用 ==!= 比较迭代器。
示例:使用迭代器遍历 std::vector
std::vector<int> vec = {1, 2, 3, 4, 5};
for(std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

3. 迭代器的高级应用

应用:迭代器用于容器的许多操作,如插入、删除元素。

示例:使用迭代器进行插入和删除
vec.insert(vec.begin() + 2, 6); // 在第三个元素前插入 6
vec.erase(vec.begin()); // 删除第一个元素

4. 注意事项

注意

  • 容器被修改时,迭代器可能失效。
  • 某些容器(如 std::list)不支持随机访问迭代器。

完整代码示例

#include 
#include 

int main() {
    // 创建一个 vector
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 使用迭代器遍历并打印元素
    std::cout << "Original vector: ";
    for(auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\\n";

    // 使用迭代器插入元素
    vec.insert(vec.begin() + 2, 6);

    // 使用迭代器删除元素
    vec.erase(vec.begin());

    // 再次打印修改后的 vector
    std::cout << "Modified vector: ";
    for(const auto& value : vec) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

Original vector: 1 2 3 4 5 
Modified vector: 2 3 6 4 5 

容器定义和初始化

  1. std::vector

    • 定义std::vector 是一个动态数组,可以存储任意数量的同类型元素。
    • 初始化
      • 空的 vectorstd::vector vec;
      • 初始化列表:std::vector vec = {1, 2, 3, 4, 5};
      • 指定大小和初始值:std::vector vec(10, 0);(10个元素,每个都是0)
  2. std::list

    • 定义std::list 是一个双向链表,适用于频繁的插入和删除操作。
    • 初始化
      • 空的 liststd::list lst;
      • 初始化列表:std::list lst = {1, 2, 3, 4, 5};
  3. std::map

    • 定义std::map 是一个基于键-值对的关联容器,每个键都是唯一的。
    • 初始化
      • 空的 mapstd::map mp;
      • 初始化列表:std::map mp = {{1, "one"}, {2, "two"}};

示例代码

以下是一些容器定义和初始化的示例代码。

#include 
#include 
#include 
#include 

int main() {
    // Vector 初始化
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::cout << "Vector: ";
    for(const int& i : vec) {
        std::cout << i << " ";
    }
    std::cout << "\\n";

    // List 初始化
    std::list<int> lst = {1, 2, 3, 4, 5};
    std::cout << "List: ";
    for(const int& i : lst) {
        std::cout << i << " ";
    }
    std::cout << "\\n";

    // Map 初始化
    std::map<int, std::string> mp = {{1, "one"}, {2, "two"}};
    std::cout << "Map: ";
    for(const auto& pair : mp) {
        std::cout << pair.first << "->" << pair.second << " ";
    }
    std::cout << std::endl;

    return 0;
}

解释

  • std::vector vec = {1, 2, 3, 4, 5};

    • 这行代码创建了一个 std::vector,并使用初始化列表 {1, 2, 3, 4, 5} 进行初始化。
  • std::list lst = {1, 2, 3, 4, 5};

    • 类似地,这行代码创建了一个 std::list 并使用同样的初始化列表。
  • std::map mp = {{1, "one"}, {2, "two"}};

    • 这里创建了一个 std::map,其中每个元素都是一个键-值对。

C++ 容器赋值和 Swap

在 C++ 标准模板库(STL)中,容器支持赋值操作和 swap 函数。这些操作是容器类的基本功能,允许容器间的内容交换和赋值。以下是这些操作的详细介绍:

1. 赋值操作

赋值:容器可以使用赋值操作符(=)将一个容器的内容复制到另一个容器中。

示例:std::vector 的赋值
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2;
vec2 = vec1; // 将 vec1 的内容赋值给 vec2

2. Swap 操作

swap()swap 函数交换两个同类型容器的内容。这是一个非常高效的操作,通常只交换容器内部的指针,而不是复制整个容器的数据。

示例:使用 swap 交换 std::vector 的内容
std::vector<int> vec3 = {4, 5, 6};
vec1.swap(vec3); // 交换 vec1 和 vec3 的内容

完整代码示例

下面是一个综合示例,演示了赋值和 swap 操作。

#include 
#include 

int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2;
    std::vector<int> vec3 = {4, 5, 6};

    // 赋值操作
    vec2 = vec1; // 将 vec1 的内容赋值给 vec2

    // 输出赋值后的 vec2
    std::cout << "After assignment, vec2: ";
    for(const auto& value : vec2) {
        std::cout << value << " ";
    }
    std::cout << "\\n";

    // Swap 操作
    vec1.swap(vec3); // 交换 vec1 和 vec3 的内容

    // 输出 swap 后的 vec1 和 vec3
    std::cout << "After swap, vec1: ";
    for(const auto& value : vec1) {
        std::cout << value << " ";
    }
    std::cout << "\\n";

    std::cout << "After swap, vec3: ";
    for(const auto& value : vec3) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

After assignment, vec2: 1 2 3 
After swap, vec1: 4 5 6 
After swap, vec3: 1 2 3 
  • 赋值操作:将 vec1 的内容赋值给 vec2,使得 vec2 的内容变为 1 2 3
  • Swap 操作:交换 vec1vec3 的内容,使得 vec1 的内容变为 4 5 6,而 vec3 的内容变为 1 2 3

容器大小操作

在 C++ 标准模板库(STL)中,容器提供了多种操作来管理和查询其大小。这些操作允许你检查容器是否为空,获取容器的大小,调整容器的大小,以及在容器的末尾添加或删除元素。以下是这些操作的详细介绍:

1. 检查是否为空

empty():这个函数用于检查容器是否为空(即不包含任何元素)。

示例:检查 std::vector 是否为空
std::vector<int> vec;
bool isEmpty = vec.empty(); // 检查 vec 是否为空

2. 获取大小

size():返回容器中元素的数量。

示例:获取 std::vector 的大小
std::vector<int> vec = {1, 2, 3};
size_t size = vec.size(); // 获取 vec 的大小

3. 改变大小

resize():改变容器的大小。如果新大小大于当前大小,则在容器末尾添加元素。如果新大小小于当前大小,则从容器末尾删除元素。

示例:改变 std::vector 的大小
vec.resize(5); // 改变 vec 的大小为 5

4. 添加和删除元素

push_back():在容器末尾添加一个新元素。
pop_back():删除容器末尾的元素。

示例:在 std::vector 中添加和删除元素
vec.push_back(4); // 在 vec 末尾添加元素 4
vec.pop_back(); // 删除 vec 末尾的元素

完整代码示例

下面是一个综合示例,演示了上述所有大小操作。

#include 
#include 

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 检查是否为空
    std::cout << "Is empty: " << vec.empty() << "\\n";

    // 获取大小
    std::cout << "Size before resize: " << vec.size() << "\\n";

    // 改变大小
    vec.resize(5); // 改变 vec 的大小为 5

    // 输出改变大小后的 vec
    std::cout << "Size after resize: " << vec.size() << "\\n";
    std::cout << "Elements: ";
    for(const auto& value : vec) {
        std::cout << value << " ";
    }
    std::cout << "\\n";

    // 添加和删除元素
    vec.push_back(4); // 添加元素
    vec.pop_back(); // 删除元素

    // 输出最终的 vec
    std::cout << "Final elements: ";
    for(const auto& value : vec) {
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

Is empty: 0
Size before resize: 3
Size after resize: 5
Elements: 1 2 3 0 0 
Final elements: 1 2 3 0 0 
  • 检查是否为空vec 不为空(Is empty: 00 表示 false)。
  • 获取大小:初始 vec 的大小为 3
  • 改变大小:改变后的 vec 大小为 5,多出的位置用默认值 0 填充。
  • 添加和删除元素:添加了一个元素 4 然后又删除了,所以最终元素没有变化。

顺序容器操作

在 C++ 的标准模板库(STL)中,顺序容器如 vectordequelist 等提供了一系列操作,使得元素的管理和访问变得灵活高效。以下是顺序容器的一些常用操作的详细介绍:

1. 访问元素

operator[]at():用于访问指定位置的元素。at() 方法与 operator[] 相似,但它会检查索引是否越界。

示例:访问 std::vector 中的元素
std::vector<int> vec = {1, 2, 3};
int firstElement = vec[0]; // 使用 operator[]
int secondElement = vec.at(1); // 使用 at()

2. 前后元素访问

front()back():分别返回容器中的第一个和最后一个元素的引用。

示例:访问 std::vector 的第一个和最后一个元素
int first = vec.front(); // 第一个元素
int last = vec.back(); // 最后一个元素

3. 迭代器

begin(), end(), rbegin(), rend():分别返回指向容器第一个元素、尾后元素的迭代器,以及对应的反向迭代器。

示例:遍历 std::vector
for(auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

4. 修改容器

insert(), erase(), push_back(), pop_back(), clear():用于在容器中插入和删除元素。

示例:修改 std::vector
vec.push_back(4); // 添加元素
vec.erase(vec.begin()); // 删除第一个元素
vec.clear(); // 清空容器

5. 容器大小

size(), empty(), resize():获取容器的大小,检查是否为空,或者改变容器的大小。

示例:检查 std::vector 的大小和状态
bool isEmpty = vec.empty(); // 检查是否为空
size_t size = vec.size(); // 获取大小
vec.resize(3); // 调整大小

完整代码示例

下面是一个综合示例,展示了上述所有操作。

#include 
#include 

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 访问元素
    std::cout << "First element: " << vec[0] << ", Second element: " << vec.at(1) << "\\n";

    // 前后元素访问
    std::cout << "Front: " << vec.front() << ", Back: " << vec.back() << "\\n";

    // 遍历容器
    std::cout << "Elements: ";
    for(auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\\n";

    // 修改容器
    vec.push_back(4);
    vec.erase(vec.begin());
    vec.clear();

    // 容器大小操作
    std::cout << "Is empty: " << vec.empty() << "\\n";
    std::cout << "Size: " << vec.size() << "\\n";
    vec.resize(3);

    return 0;
}

输出:

First element: 1, Second element: 2
Front: 1, Back: 3
Elements: 1 2 3 
Is empty: 1
Size: 0

特殊的 forward_list 操作

在 C++ 标准模板库(STL)中,std::forward_list 是一个单向链表,它提供了一些特殊的操作,适用于单向链表的特性。这些操作与其他顺序容器(如 std::vectorstd::list)有所不同。以下是 std::forward_list 的一些特殊操作的详细介绍:

1. 在指定位置前插入元素

insert_after():在指定迭代器位置之后插入一个或多个元素。

示例:在 std::forward_list 中插入元素
std::forward_list<int> flist = {1, 2, 3};
auto it = flist.begin(); // 获取迭代器指向第一个元素
flist.insert_after(it, 4); // 在第一个元素之后插入 4

2. 删除元素

erase_after():删除指定迭代器之后的元素。

示例:删除 std::forward_list 中的元素
flist.erase_after(it); // 删除第一个元素之后的元素

3. 改变容器大小

由于 std::forward_list 是单向链表,它没有提供 resize() 方法。相应的,你需要使用迭代器或其他方法来改变大小。

4. 添加和删除元素

push_front()pop_front():在容器的开头添加或删除元素。

示例:在 std::forward_list 的开头添加和删除元素
flist.push_front(0); // 在开始处添加 0
flist.pop_front(); // 删除开始处的元素

完整代码示例

下面是一个综合示例,展示了 std::forward_list 的这些特殊操作。

#include 
#include 

int main() {
    std::forward_list<int> flist = {1, 2, 3};

    // 插入元素
    auto it = flist.begin(); // 迭代器指向第一个元素
    flist.insert_after(it, 4); // 在第一个元素之后插入 4

    // 删除元素
    flist.erase_after(it); // 删除第一个元素之后的元素

    // 添加和删除元素
    flist.push_front(0); // 在开始处添加 0
    flist.pop_front(); // 删除开始处的元素

    // 输出 forward_list
    std::cout << "Forward list elements: ";
    for(int elem : flist) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

Forward list elements: 1 2 3 

容器操作可能使迭代器失效

在 C++ 中使用标准模板库(STL)的容器时,特别需要注意容器操作可能导致迭代器失效的情况。迭代器失效意味着迭代器不再指向有效的容器元素或容器的末尾。以下是导致迭代器失效的一些常见原因及其解释:

1. 添加或删除元素

影响:在 std::vectorstd::stringstd::deque 等容器中添加或删除元素可能导致所有指向容器元素的迭代器、指针和引用失效。

示例:std::vector 添加元素导致迭代器失效
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 迭代器指向第一个元素
vec.push_back(4); // 可能导致 it 失效

2. std::vectorstd::string 的动态内存重新分配

影响:如果容器需要扩展其底层的动态数组来容纳更多元素,所有现有迭代器、指针和引用都将失效。

3. std::liststd::forward_list 的元素删除

影响:在 std::liststd::forward_list 中删除元素会导致指向被删除元素的迭代器失效,但其他迭代器不受影响。

示例:std::list 删除元素导致迭代器失效
std::list<int> lst = {1, 2, 3};
auto it = lst.begin(); // 迭代器指向第一个元素
lst.erase(it); // it 现在失效

4. std::mapstd::set 的元素删除

影响:删除 std::mapstd::set 中的元素会导致指向该元素的迭代器失效,但其他迭代器不受影响。

防止迭代器失效的策略

  • 使用返回的迭代器:某些操作,如 erase(),返回新的有效迭代器。
  • 避免在容器迭代过程中进行插入或删除:在迭代容器时,避免执行可能导致迭代器失效的操作。
  • 使用索引而非迭代器:对于支持随机访问的容器,比如 std::vector,使用索引而不是迭代器进行元素访问。

完整代码示例

这是一个展示如何处理迭代器可能失效的情况的示例:

#include 
#include 
#include 

int main() {
    // Vector 示例
    std::vector<int> vec = {1, 2, 3};
    vec.push_back(4); // 在迭代后添加元素,避免迭代器失效

    // List 示例
    std::list<int> lst = {1, 2, 3};
    auto it = lst.begin();
    it = lst.erase(it); // 使用返回的迭代器

    // 输出结果
    std::cout << "Vector: ";
    for (int v : vec) {
        std::cout << v << " ";
    }
    std::cout << "\nList: ";
    for (int l : lst) {
        std::cout << l << " ";
    }
    std::cout << std::endl;

    return 0;
}

输出:

Vector: 1 2 3 4 
List: 2 3 

在这个示例中,std::vector 在迭代之后进行了添加操作,以避免迭代器失效。对于 std::list,使用 erase() 方法返回的新迭代器来避免迭代器失效的问题。

Vector 对象的增长机制

在 C++ 中,std::vector 是一个动态数组,它可以在运行时根据需要自动调整其大小。std::vector 的增长机制是通过动态内存分配和拷贝构造来实现的。以下是 std::vector 增长过程的详细介绍:

1. 动态内存分配

当向 std::vector 添加元素且当前容量不足以容纳更多元素时,它会执行一次动态内存分配。这个过程包括以下几个步骤:

a. 计算新的容量

新的容量通常是当前容量的两倍,这种增长策略旨在平衡内存使用和复制操作的次数,以实现渐进式扩展。这种增长策略称为几何增长

b. 分配新内存

std::vector 会分配一个更大的内存块来存储其元素。新分配的内存大小至少与计算出的新容量一致。

c. 移动元素

现有元素被移动或复制到新的内存位置。这通常通过调用元素的移动构造函数或复制构造函数完成。

d. 释放旧内存

一旦所有元素都移动到新的内存位置,std::vector 会释放原来的内存块。

2. 复杂性和性能

由于重新分配涉及复制或移动所有现有元素到新的内存位置,因此这个操作的时间复杂度是线性的,即 O(n),其中 n 是 std::vector 中的元素数量。然而,由于几何增长策略,这种重分配不是经常发生的。这意味着平均情况下,向 std::vector 添加一个元素的时间复杂度仍然是常数级的,即 O(1)。

3. 保留额外容量

通过 reserve() 成员函数,可以预先分配足够的内存以避免频繁的内存重新分配。这在已知 std::vector 将要存储大量元素时非常有用。

示例

#include 
#include 

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

    // 初始容量可能为零
    std::cout << "Initial capacity: " << vec.capacity() << std::endl;

    // 添加元素,触发容量增长
    for(int i = 0; i < 10; ++i) {
        vec.push_back(i);
        std::cout << "Capacity after adding element " << i << ": " << vec.capacity() << std::endl;
    }

    return 0;
}

在这个示例中,每次向 std::vector 添加元素时,打印其容量,以观察其如何增长。不过要注意的是,具体的增长策略可能因编译器和库的实现而异。

额外的 string 操作

在 C++ 中,std::string 类提供了许多强大的操作,用于处理和操作字符串数据。除了基本的赋值、访问和大小操作之外,std::string 还提供了一些额外的功能,使得字符串处理更加灵活和高效。以下是一些常用的额外 std::string 操作:

1. 字符串连接

operator+=append():用于连接字符串或字符到现有的 std::string 对象。

示例:字符串连接
std::string str = "Hello";
str += ", World!"; // 使用 operator+= 进行连接
str.append(" Welcome."); // 使用 append() 方法进行连接

2. 查找和替换

  • find():查找子字符串或字符在字符串中的位置。
  • replace():替换字符串中的某部分为另一个字符串。
示例:查找和替换
std::string str2 = "Hello, World!";
size_t pos = str2.find("World"); // 查找 "World"
if (pos != std::string::npos) {
    str2.replace(pos, 5, "C++"); // 替换 "World" 为 "C++"
}

3. 插入和删除

  • insert():在指定位置插入字符串或字符。
  • erase():删除字符串中的一部分。
示例:插入和删除
std::string str3 = "Hello, World!";
str3.insert(6, "C++ "); // 在位置 6 插入 "C++ "
str3.erase(0, 6); // 删除从位置 0 开始的 6 个字符

4. 子字符串

substr():从字符串中提取子字符串。

示例:提取子字符串
std::string str4 = "Hello, World!";
std::string sub = str4.substr(7, 5); // 提取从位置 7 开始的 5 个字符

5. 字符串比较

compare():比较两个字符串。

示例:字符串比较
std::string str5 = "Hello";
int result = str5.compare("Hello"); // 比较字符串

完整代码示例

这是一个展示上述 std::string 操作的综合示例:

#include 
#include 

int main() {
    // 字符串连接
    std::string str = "Hello";
    str += ", World!";
    str.append(" Welcome.");

    // 查找和替换
    std::string str2 = "Hello, World!";
    size_t pos = str2.find("World");
    if (pos != std::string::npos) {
        str2.replace(pos, 5, "C++");
    }

    // 插入和删除
    std::string str3 = "Hello, World!";
    str3.insert(6, "C++ ");
    str3.erase(0, 6);

    // 提取子字符串
    std::string str4 = "Hello, World!";
    std::string sub = str4.substr(7, 5);

    // 字符串比较
    std::string str5 = "Hello";
    int result = str5.compare("Hello");

    // 输出结果
    std::cout << "Concatenated string: " << str << std::endl;
    std::cout << "Replaced string: " << str2 << std::endl;
    std::cout << "Modified string: " << str3 << std::endl;
    std::cout << "Substring: " << sub << std::endl;
    std::cout << "Comparison result: " << result << std::endl;

    return 0;
}

输出:

Concatenated string: Hello, World! Welcome.
Replaced string: Hello, C++!
Modified string: C++ World!
Substring: World
Comparison result: 0

容器适配器

在 C++ 标准模板库(STL)中,容器适配器是一种特殊类型的容器,它在现有容器的基础上提供了一定的功能封装。容器适配器并不是真正的容器,而是在一定程度上改变或增加了基础容器的行为。常见的容器适配器包括 stackqueuepriority_queue

1. Stack(栈)

特性std::stack 提供了后进先出(LIFO)的数据结构。它只允许从一端(顶部)进行元素的添加和移除。

  • 基础容器:默认情况下,stack 是基于 std::deque 实现的,但也可以使用 std::liststd::vector
  • 主要操作
    • push():在栈顶添加一个元素。
    • pop():移除栈顶元素。
    • top():访问栈顶元素。
    • empty()size():检查栈是否为空,获取栈中元素的数量。
示例:使用 std::stack
#include 

std::stack<int> stack;
stack.push(1); // 添加元素
stack.push(2);
stack.pop(); // 移除元素
int top = stack.top(); // 访问栈顶元素

2. Queue(队列)

特性std::queue 提供了先进先出(FIFO)的数据结构。它允许在一端(队尾)添加元素,在另一端(队头)移除元素。

  • 基础容器:默认情况下,queue 是基于 std::deque 实现的,但也可以使用 std::list
  • 主要操作
    • push():在队尾添加一个元素。
    • pop():移除队头的元素。
    • front()back():访问队头和队尾元素。
    • empty()size():检查队列是否为空,获取队列中元素的数量。
示例:使用 std::queue
#include 

std::queue<int> queue;
queue.push(1); // 添加元素
queue.push(2);
queue.pop(); // 移除元素
int front = queue.front(); // 访问队头元素

3. Priority Queue(优先队列)

特性std::priority_queue 提供了一种方式,使得队头总是包含最大元素(或根据指定的比较函数确定的顺序)的队列。

  • 基础容器:默认情况下,priority_queue 是基于 std::vector 实现的,但也可以使用 std::deque
  • 主要操作
    • push():添加一个元素。
    • pop():移除最大元素(位于队头)。
    • top():访问最大元素。
    • empty()size():检查优先队列是否为空,获取元素的数量。
示例:使用 std::priority_queue
#include 

std::priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(2);
int top = pq.top(); // top 现在是 3
pq.pop(); // 移除最大元素

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