C++ 标准库容器

标准库为相关对象的存储集合提供了各种类型安全容器。 容器是类模板。 在声明容器变量时,可以指定容器将保存的元素的类型。 可以使用初始值设定项列表构造容器。 它们具有用于添加和移除元素以及执行其他操作的成员函数。

可使用迭代器循环访问容器中的元素以及访问单个元素。 可以通过使用其成员函数和运算符以及全局函数来显式使用迭代器。 还可以隐式使用它们,例如通过使用范围 for 循环。 所有 C++ 标准库容器的迭代器都有一个通用接口,但是每个容器都会定义自己的专用迭代器。

容器可以分为三个类别:序列容器、关联容器和容器适配器。

序列容器

序列容器维护你指定的插入元素的顺序。

vector 容器的行为类似于数组,但可以根据要求自动增长。 它可以随机访问、连续存储,长度也非常灵活。 基于上述和其他原因,vector 是多数应用程序的首选序列容器。 若不确定要使用哪种序列容器,请首先使用矢量! 有关详细信息,请参阅 vector

array 容器具备 vector 的某些优点,但长度不够灵活。 有关详细信息,请参阅 array

deque(双端队列)容器支持在容器的起点和终点进行快速插入和删除。 它享有 vector 随机访问和长度灵活的优点,但是不具备连续性。 有关详细信息,请参阅 deque

list 容器是双向链接列表,在容器内的任意位置启用了双向访问、快速插入和快速删除,但你不能随机访问此容器中的元素。 有关详细信息,请参阅 list

forward_list 容器是单独链表,list 的向前访问版本。 有关详细信息,请参阅 forward_list

关联容器

在关联容器中,按照预定义的顺序插入元素,例如按升序排序。 无序的关联容器也可用。 关联容器可分为两个子集:映射和组集。

map,有时称为字典,包含键/值对。 键用于对序列排序,值与该键关联。 例如,map可能包含许多键(代表文本中每个独特的单词)和相应的值(代表每个单词在文本中出现的次数)。 map的无序版本是 unordered_map。 有关详细信息,请参阅 mapunordered_map

set仅是按升序排列每个元素的容器,值也是键。 set的无序版本是 unordered_set。 有关详细信息,请参阅 setunordered_set

mapset 都仅允许将键或元素的一个实例插入容器中。 如果需要元素的多个实例,请使用multimapmultiset。 无序版本是 unordered_multimapunordered_multiset。 有关详细信息,请参阅 multimapunordered_multimapmultisetunordered_multiset

有序的映射和组集支持双向迭代器,其未排序副本支持向前迭代器。 有关更多信息,请参见 迭代器

关联容器中的异类查找 (C++14)

排序关联容器(映射、多重映射、集与多重集)现在支持异类查找,这意味着,你将不再需要将完全相同的对象类型作为键或元素在成员函数(如 find()lower_bound())中传递。 相反,可以传递为定义了重载 operator< 以启用对键类型的比较的类型。

如果在声明容器变量时指定 std::less<>std::greater<>“菱形函子”比较运算符,则会在选择加入基础上启用异类查询,如下所示:

std::set<BigObject, std::less<>> myNewSet;

如果你使用默认的比较运算符,该容器的行为会与在 C++11 和更早版本中完全一样。

下面的示例演示如何重载 operator<,以使 std::set 的用户能够只需通过传入一个可以与每个对象的 BigObject::id 成员进行比较的小字符串,来执行查找操作。

#include <set>
#include <string>
#include <iostream>
#include <functional>

using namespace std;

class BigObject
{
public:
    string id;
    explicit BigObject(const string& s) : id(s) {}
    bool operator< (const BigObject& other) const
    {
        return this->id < other.id;
    }

    // Other members....
};

inline bool operator<(const string& otherId, const BigObject& obj)
{
    return otherId < obj.id;
}

inline bool operator<(const BigObject& obj, const string& otherId)
{
    return obj.id < otherId;
}

int main()
{
    // Use C++14 brace-init syntax to invoke BigObject(string).
    // The s suffix invokes string ctor. It is a C++14 user-defined
    // literal defined in <string>
    BigObject b1{ "42F"s };
    BigObject b2{ "52F"s };
    BigObject b3{ "62F"s };
    set<BigObject, less<>> myNewSet; // C++14
    myNewSet.insert(b1);
    myNewSet.insert(b2);
    myNewSet.insert(b3);
    auto it = myNewSet.find(string("62F"));
    if (it != myNewSet.end())
        cout << "myNewSet element = " << it->id << endl;
    else
        cout << "element not found " << endl;

    // Keep console open in debug mode:
    cout << endl << "Press Enter to exit.";
    string s;
    getline(cin, s);
    return 0;
}

//Output: myNewSet element = 62F

已重载映射、多重映射、集与多重集中的下列成员函数来支持异类查找:

  1. find

  2. count

  3. lower_bound

  4. upper_bound

  5. equal_range

容器适配器

容器适配器是序列容器或关联容器的变体,为了简单明确起见,它对接口进行限制。 容器适配器不支持迭代器。

queue容器遵循 FIFO(先进先出)语义。 第一个推送(即插入队列中)的元素将第一个弹出(即从队列中删除)。 有关详细信息,请参阅 queue

priority_queue容器也是如此组织,因此具有最高值的元素始终排在队列的第一位。 有关详细信息,请参阅 priority_queue

stack容器遵循 LIFO(后进先出)语义。 堆栈上最后推送的元素将第一个弹出。 有关详细信息,请参阅 stack

由于容器适配器不支持迭代器,因此它们无法与 C++ 标准库算法一起使用。 有关详细信息,请参阅算法

容器元素的需求

通常,如果插入 C++ 标准库容器中的元素可复制,那么这些元素可以是任何对象类型。 只要你不调用尝试复制元素的成员函数,仅可移动的元素(例如,那些类似于 vector<unique_ptr<T>>、使用 unique_ptr<> 创建的元素)会一直工作。

析构函数不允许引发异常。

有序的关联容器(本文之前所述)必须已定义公共比较运算符。 默认情况下,该运算符是 operator<,即使不能与 operator< 共同使用的类型也会受支持。

容器中的某些操作可能还需要公共默认构造函数和公共等效运算符。 例如,未排序的关联容器需要支持相等性和哈希处理。

正在访问容器元素

使用迭代器访问容器的元素。 有关更多信息,请参见 迭代器

注意

还可以使用基于范围的 for 循环来循环访问 C++ 标准库集合。

比较容器

所有容器都重载运算符 == 用于比较同一类型的两个具有相同的元素类型的容器。 可以使用 == 比较一个矢量 <string> 与另一个矢量 <string>,但是不能用它比较矢量 <string> 与列表 <string> 或比较矢量 <string> 与矢量 <char*>。 在 C++98/03 中,可以使用 std::equalstd::mismatch 来比较不同的容器类型和/或元素类型。 在 C++11 中,还可以使用 std::is_permutation。 但在所有这些情况下,函数假设容器都具有相同的长度。 如果第二个范围比第一个短,则产生未定义的行为。 如果第二个范围的更长,结果可能仍然不正确,因为第一个范围结束后比较不会继续。

比较不同的容器 (C++14)

在 C++14 及更高版本中,通过使用采用两个完整范围的 std::equalstd::mismatchstd::is_permutation 函数重载之一来比较不同的容器和/或不同的元素类型。 这些重载使你能够比较具有不同长度的容器。 这些重载使用户非常不易遭受错误,并进行了优化,当比较不同长度的容器时会在固定时间内返回错误。 因此,建议使用这些重载,除非你有明确的理由不这么做,或者正在使用 std::list 容器,它不会从双范围优化中受益。

另请参阅

并行容器
<sample container>
C++ 标准库中的线程安全