C++ STL学习笔记(自己用的)

  • 可变数组
vector <int> a; //定义 a 数组,当前数组长度为 0,但和普通数组不同的是,此数组 a 可以根据存储数据的数量自动变长。
//向数组 a 中添加 10 个元素
for (int i = 0; i < 10 ; i++)
    a.push_back(i)
//还可以手动调整数组 a 的大小
a.resize(100);
a[90] = 100;
//还可以直接删除数组 a 中所有的元素,此时 a 的长度变为 0
a.clear();
//重新调整 a 的大小为 20,并存储 20 个 -1 元素。
a.resize(20, -1)

a.push_back()  // 尾插
a.begin()  //指向容器的第一个元素
a.end()   //指向容器的最后一个元素的下一个位置

//  for_each 遍历引入头文件 algorithm
  • 双向迭代器不支持用“<”进行比较:
  • 双向迭代器不支持用下标随机访问元素:
#include <iostream>
//需要引入 array 头文件
#include <array>
using namespace std;
int main()
{
    array<int, 4> values{};
    //初始化 values 容器为 {0,1,2,3}
    for (int i = 0; i < values.size(); i++) {
        values.at(i) = i;
    }
    //使用 get() 重载函数输出指定位置元素
    cout << get<0>(values) << endl;
    //如果容器不为空,则输出容器中所有的元素
    if (!values.empty()) {
        for (auto val = values.begin(); val < values.end(); val++) {
            cout << *val << " ";
        }
    }
}
  • 代码中的 auto 关键字,可以使编译器自动判定变量的类型。
  • 在 array 容器的模板类中,和随机访问迭代器相关的成员函数如下表
成员函数功能
begin()返回指向容器中第一个元素的正向迭代器;如果是 const 类型容器,在该函数返回的是常量正向迭代器。
end()返回指向容器最后一个元素之后一个位置的正向迭代器;如果是 const 类型容器,在该函数返回的是常量正向迭代器。此函数通常和 begin() 搭配使用。
rbegin()返回指向最后一个元素的反向迭代器;如果是 const 类型容器,在该函数返回的是常量反向迭代器。
rend()返回指向第一个元素之前一个位置的反向迭代器。如果是 const 类型容器,在该函数返回的是常量反向迭代器。此函数通常和 rbegin() 搭配使用。
cbegin()和 begin() 功能类似,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。
cend()和 end() 功能相同,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。
crbegin()和 rbegin() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。
crend()和 rend() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。
  • 访问array容器中单个元素
// 首先,可以通过容器名[]的方式直接访问和使用容器中的元素,这和 C++ 标准数组访问元素的方式相同,例如:
values[4] = values[3] + 2.O*values[1];
// 为了能够有效地避免越界访问的情况,可以使用 array 容器提供的 at() 成员函数,例如 :
values.at (4) = values.at(3) + 2.O*values.at(1);
// array 容器还提供了 get<n> 模板函数,它是一个辅助函数,能够获取到容器的第 n 个元素。需要注意的是,该模板函数中,参数的实参必须是一个在编译时可以确定的常量表达式,所以它不能是一个循环变量。也就是说,它只能访问模板参数指定的元素,编译器在编译时会对它进行检查。
array<string, 5> words{ "one","two","three","four","five" };
cout << get<3>(words) << endl; // Output words[3]
// array 容器提供了 data() 成员函数,通过调用该函数可以得到指向容器首个元素的指针。通过该指针,我们可以获得容器中的各个元素,例如:
array<int, 5> words{1,2,3,4,5};
cout << *( words.data()+1);  // 2
  • 访问array容器中多个元素
// array 容器提供的 size() 函数能够返回容器中元素的个数(函数返回值为 size_t 类型),所以能够像下面这样去逐个提取容器中的元素,并计算它们的和:
double total = 0;
for(size_t i = 0 ; i < values.size() ; ++i)
{
    total += values[i];
}
  • size() 函数的存在,为 array 容器提供了标准数组所没有的优势,即能够知道它包含多少元素。
  • vector 容器以类模板 vector( T 表示存储元素的类型)的形式定义在 头文件中,并位于 std 命名空间中。
  • 创建vector的几种方式
// 创建存储 double 类型元素的一个 vector 容器
std::vector<double> values;
// 创建好空容器的基础上,还可以像下面这样通过调用 reserve() 成员函数来增加容器的容量
values.reserve(20);
// 除了创建空 vector 容器外,还可以在创建的同时指定初始值以及元素个数,比如:
std::vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19};  // 含有8个元素的vector
// 在创建 vector 容器时,也可以指定元素个数:
std::vector<double> values(20);
// 注意,圆括号 () 和大括号 {} 是有区别的,前者(例如 (20) )表示元素的个数,而后者(例如 {20} ) 则表示 vector 容器中只有一个元素 20。
// 如果不想用 0 作为默认值,也可以指定一个其它值,例如:
std::vector<double> values(20, 1.0);
// 第二个参数指定了所有元素的初始值,因此这 20 个元素的值都是 1.0。
// 值得一提的是,圆括号 () 中的 2 个参数,既可以是常量,也可以用变量来表示,例如:

```cpp
int num=20;
double value =1.0;
std::vector<double> values(num, value);

  • vector容器迭代器的基本用法
#include <iostream>
//需要引入 vector 头文件
#include <vector>
using namespace std;
int main()
{
    vector<int>values{1,2,3,4,5};
    auto first = values.begin();
    auto end = values.end();
    while (first != end)
    {
        cout << *first << " ";
        ++first;
    }
    return 0;
}
  • vector 支持迭代器的成员函数
    在这里插入图片描述

  • 访问vector容器中单个元素

// vector 容器可以向普通数组那样访问存储的元素,甚至对指定下标处的元素进行修改,比如:
vector<int> values{1,2,3,4,5};
//获取容器中首个元素
cout << values[0] << endl;
//修改容器中下标为 0 的元素的值
values[0] = values[1] + values[2] + values[3] + values[4];
cout << values[0] << endl;
// 值得一提的是,容器名[n]这种获取元素的方式,需要确保下标 n 的值不会超过容器的容量(可以通过 capacity() 成员函数获取),否则会发生越界访问的错误。幸运的是,和 array 容器一样,vector 容器也提供了 at() 成员函数,当传给 at() 的索引会造成越界时,会抛出std::out_of_range异常。
vector<int> values{1,2,3,4,5};
//获取容器中首个元素
cout << values.at(0) << endl;
//修改容器中下标为 0 的元素的值
values.at(0) = values.at(1) + values.at(2) + values.at(3) + values.at(4);
cout << values.at(0) << endl;
//下面这条语句会发生 out_of_range 异常
//cout << values.at(5) << endl;
// 除此之外,vector 容器还提供了 2 个成员函数,即 front() 和 back(),它们分别返回 vector 容器中第一个和最后一个元素的引用,通过利用这 2 个函数返回的引用,可以访问(甚至修改)容器中的首尾元素。
vector<int> values{1,2,3,4,5};
cout << "values 首元素为:" << values.front() << endl;
cout << "values 尾元素为:" << values.back() << endl;
//修改首元素
values.front() = 10;
cout <<"values 新的首元素为:" << values.front() << endl;
//修改尾元素
values.back() = 20;
cout << "values 新的尾元素为:" << values.back() << endl;
// vector 容器还提供了 data() 成员函数,该函数的功能是返回指向容器中首个元素的指针。通过该指针也可以访问甚至修改容器中的元素。比如:
vector<int> values{1,2,3,4,5};
//输出容器中第 3 个元素的值
cout << *(values.data() + 2) << endl;
//修改容器中第 2 个元素的值
*(values.data() + 1) = 10;
cout << *(values.data() + 1) << endl;
// 如果想访问 vector 容器中多个元素,可以借助 size() 成员函数,该函数可以返回 vector 容器中实际存储的元素个数。例如
vector<int> values{1,2,3,4,5};
//从下标 0 一直遍历到 size()-1 处
for (int i = 0; i < values.size(); i++) {
    cout << values[i] << " ";
}
// 注意,这里不要使用 capacity() 成员函数,因为它返回的是 vector 容器的容量,而不是实际存储元素的个数,这两者是有差别的。
// 还可以使用 vector 迭代器遍历 vector 容器,这里以 begin()/end() 为例
vector<int> values{1,2,3,4,5};
for (auto first = values.begin(); first < values.end(); ++first) {
    cout << *first << " "
}

  • emplace_back()和push_back()的区别
    emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

  • 因此,在实际使用时,建议优先选用 emplace_back()。

  • insert() 函数的功能是在 vector 容器的指定位置插入一个或多个元素。
    在这里插入图片描述

std::vector<int> demo{1,2};
//第一种格式用法
demo.insert(demo.begin() + 1, 3);//{1,3,2}
//第二种格式用法
demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}
//第三种格式用法
std::array<int,3>test{ 7,8,9 };
demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
//第四种格式用法
demo.insert(demo.end(), { 10,11 });//{1,3,2,5,5,7,8,9,10,11}

  • emplace() 是 C++ 11 标准新增加的成员函数,用于在 vector 容器指定位置之前插入一个新的元素。
    再次强调,emplace() 每次只能插入一个元素,而不是多个。
    该函数的语法格式如下:iterator emplace (const_iterator pos, args…);
    其中,pos 为指定插入位置的迭代器;args… 表示与新插入元素的构造函数相对应的多个参数;该函数会返回表示新插入元素位置的迭代器。
std::vector<int> demo1{1,2};
//emplace() 每次只能插入一个 int 类型元素
demo1.emplace(demo1.begin(), 3);
  • 通过 insert() 函数向 vector 容器中插入 testDemo 类对象,需要调用类的构造函数和移动构造函数(或拷贝构造函数);而通过 emplace() 函数实现同样的功能,只需要调用构造函数即可。
    因此,在实际使用中,推荐大家优先使用 emplace()
    insert:
    调用构造函数
    调用移动构造函数
    emplace:
    调用构造函数
  • vector删除元素的几种方式
    在这里插入图片描述
vector<int>demo{ 1,2,3,4,5 };
demo.pop_back();
  • 删除 vector 容器中指定位置处的元素,可以使用 erase() 成员函数,该函数的语法格式为:iterator erase (pos);
  • pos 为指定被删除元素位置的迭代器,同时该函数会返回一个指向删除元素所在位置下一个位置的迭代器。
vector<int>demo{ 1,2,3,4,5 };
auto iter = demo.erase(demo.begin() + 1);//删除元素 2
//输出 dmeo 容器新的size
cout << "size is :" << demo.size() << endl;
//输出 demo 容器新的容量
cout << "capacity is :" << demo.capacity() << endl;
for (int i = 0; i < demo.size(); i++) {
    cout << demo[i] << " ";
}
//iter迭代器指向元素 3
cout << endl << *iter << endl;

  • deque容器
// 创建一个没有任何元素的空 deque 容器:
std::deque<int> d;
// 创建一个具有 n 个元素的 deque 容器,其中每个元素都采用对应类型的默认值:
std::deque<int> d(10);   //此行代码创建一个具有 10 个元素(默认都为 0)的 deque 容器。
std::deque<int> d(10, 5) //如此就创建了一个包含 10 个元素(值都为 5)的 deque 容器。
// 在已有 deque 容器的情况下,可以通过拷贝该容器创建一个新的 deque 容器,例如:
std::deque<int> d1(5);
std::deque<int> d2(d1);
// 通过拷贝其他类型容器中指定区域内的元素(也可以是普通数组),可以创建一个新容器,例如:
//拷贝普通数组,创建deque容器
int a[] = { 1,2,3,4,5 };
std::deque<int>d(a, a + 5);
//适用于所有类型的容器
std::array<int, 5>arr{ 11,12,13,14,15 };
std::deque<int>d(arr.begin()+2, arr.end());//拷贝arr容器中的{13,14,15}
  • deque容器可利用的成员函数
    在这里插入图片描述

  • deque 支持迭代器的成员函数
    在这里插入图片描述

  • deque容器迭代器的基本用法

#include <iostream>
#include <deque>
using namespace std;
int main()
{
    deque<int>d{1,2,3,4,5};
    //从容器首元素,遍历至最后一个元素
    for (auto i = d.begin(); i < d.end(); i++) {
        cout << *i << " ";
    }
    return 0;
}
  • deque容器访问元素(4种方法)
  • 和 array、vector 容器一样,可以采用普通数组访问存储元素的方式,访问 deque 容器中的元素,比如:
    dequed{ 1,2,3,4 };
    cout << d[1] << endl;
  • at() 成员函数会自行判定访问位置是否越界,如果越界则抛出std::out_of_range异常。例如:
    dequed{ 1,2,3,4 };
    cout << d.at(1) << endl;
    d.at(1) = 5;
    cout << d.at(1) << endl;
    //下面这条语句会抛出 out_of_range 异常
    //cout << d.at(10) << endl;
  • 除此之外,deque 容器还提供了 2 个成员函数,即 front() 和 back(),它们分别返回 vector 容器中第一个和最后一个元素的引用,通过利用它们的返回值,可以访问(甚至修改)容器中的首尾元素。
    deque d{ 1,2,3,4,5 };
    cout << “deque 首元素为:” << d.front() << endl;
    cout << “deque 尾元素为:” << d.back() << endl;
    //修改首元素
    d.front() = 10;
    cout << “deque 新的首元素为:” << d.front() << endl;
    //修改尾元素
    d.back() = 20;
    cout << “deque 新的尾元素为:” << d.back() << endl;
  • 和 vector 容器不同,deque 容器没有提供 data() 成员函数,同时 deque 容器在存储元素时,也无法保证其会将元素存储在连续的内存空间中,因此尝试使用指针去访问 deque 容器中指定位置处的元素,是非常危险的。
  • 结合 deque 模板类中和迭代器相关的成员函数,可以实现遍历 deque 容器中指定区域元素的方法。
deque<int> d{ 1,2,3,4,5 };
//从元素 2 开始遍历
auto first = d.begin() + 1;
//遍历至 5 结束(不包括 5)
auto end = d.end() - 1;
while (first < end) {
    cout << *first << " ";
    ++first;
}
  • deque容器添加和删除元素方法完全攻略
    在这里插入图片描述
#include <deque>
#include <iostream>
using namespace std;
int main()
{
    deque<int>d;
    //调用push_back()向容器尾部添加数据。
    d.push_back(2); //{2}
    //调用pop_back()移除容器尾部的一个数据。
    d.pop_back(); //{}
    //调用push_front()向容器头部添加数据。
    d.push_front(2);//{2}
    //调用pop_front()移除容器头部的一个数据。
    d.pop_front();//{}
    //调用 emplace 系列函数,向容器中直接生成数据。
    d.emplace_back(2); //{2}
    d.emplace_front(3); //{3,2}
    //emplace() 需要 2 个参数,第一个为指定插入位置的迭代器,第二个是插入的值。
    d.emplace(d.begin() + 1, 4);//{3,4,2}
    for (auto i : d) {
        cout << i << " ";
    }
    //erase()可以接受一个迭代器表示要删除元素所在位置
    //也可以接受 2 个迭代器,表示要删除元素所在的区域。
    d.erase(d.begin());//{4,2}
    d.erase(d.begin(), d.end());//{},等同于 d.clear()
    return 0;
}
  • insert() 成员函数语法格式
    在这里插入图片描述
#include <iostream>
#include <deque>
#include <array>
using namespace std;
int main()
{
    std::deque<int> d{ 1,2 };
    //第一种格式用法
    d.insert(d.begin() + 1, 3);//{1,3,2}
    //第二种格式用法
    d.insert(d.end(), 2, 5);//{1,3,2,5,5}
    //第三种格式用法
    std::array<int, 3>test{ 7,8,9 };
    d.insert(d.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
    //第四种格式用法
    d.insert(d.end(), { 10,11 });//{1,3,2,5,5,7,8,9,10,11}
    for (int i = 0; i < d.size(); i++) {
        cout << d[i] << " ";
    }
    return 0;
}
  • 相比和它同功能的函数,emplace 系列函数都只调用了构造函数,而没有调用移动构造函数,这无疑提高了代码的运行效率。
  • list(STL list)容器
  • 创建一个没有任何元素的空 list 容器:
    std::list values;
  • 创建一个包含 n 个元素的 list 容器:
    std::list values(10); (int类型的默认值为 0)
    std::list values(10, 5); 创建了一个包含 10 个元素并且值都为 5 个 values 容器。
  • 在已有 list 容器的情况下,通过拷贝该容器可以创建新的 list 容器。例如:
    std::list value1(10);
    std::list value2(value1);
  • 通过拷贝其他类型容器(或者普通数组)中指定区域内的元素,可以创建新的 list 容器。例如:
    //拷贝普通数组,创建list容器
    int a[] = { 1,2,3,4,5 };
    std::list values(a, a+5);
    //拷贝其它类型的容器,创建 list 容器
    std::array<int, 5>arr{ 11,12,13,14,15 };
    std::listvalues(arr.begin()+2, arr.end());//拷贝arr容器中的{13,14,15}
  • list容器可用的成员函数
    在这里插入图片描述
  • 如下代码演示了表 中部分成员函数的用法:
#include <iostream>
#include <list>
using namespace std;
int main()
{
    //创建空的 list 容器
    std::list<double> values;
    //向容器中添加元素
    values.push_back(3.1);
    values.push_back(2.2);
    values.push_back(2.9);
    cout << "values size:" << values.size() << endl;
    //对容器中的元素进行排序
    values.sort();
    //使用迭代器输出list容器中的元素
    for (std::list<double>::iterator it = values.begin(); it != values.end(); ++it) {
        std::cout << *it << " ";
    }
    return 0;
}
  • 只有运用迭代器,才能访问 list 容器中存储的各个元素。list 模板类提供了如下表所示的这些迭代器函数。
    在这里插入图片描述
  • list(STL list)访问元素的几种方法
  • list 容器不支持随机访问,未提供下标操作符 [] 和 at() 成员函数,也没有提供 data() 成员函数。
  • 通过 front() 和 back() 成员函数,可以分别获得 list 容器中第一个元素和最后一个元素的引用形式。举个例子:
#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> mylist{ 1,2,3,4 };
    int &first = mylist.front();
    int &last = mylist.back();
    cout << first << " " << last << endl;
    first = 10;
    last = 20;
    cout << mylist.front() << " " << mylist.back() << endl;
    return 0;
}
  • 除此之外,如果想访问 list 容存储的其他元素,就只能使用 list 容器的迭代器。例如:
#include <iostream>
#include <list>
using namespace std;
int main()
{
    const std::list<int> mylist{1,2,3,4,5};
    auto it = mylist.begin();
    cout << *it << " ";
    ++it;
    while (it!=mylist.end())
    {
        cout << *it << " ";
        ++it;  
    }
    return 0;
}
  • list 模板类中,与“添加或插入新元素”相关的成员方法有如下几个:
    push_front():向 list 容器首个元素前添加新元素;
    push_back():向 list 容器最后一个元素后添加新元素;
    emplace_front():在容器首个元素前直接生成新的元素;
    emplace_back():在容器最后一个元素后直接生成新的元素;
    emplace():在容器的指定位置直接生成新的元素;
    insert():在指定位置插入新元素;
    splice():将其他 list 容器存储的多个元素添加到当前 list 容器的指定位置处。
  • 以上这些成员方法中,除了 insert() 和 splice() 方法有多种语法格式外,其它成员方法都仅有 1 种语法格式,下面程序演示了它们的具体用法。
#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> values{1,2,3};
    values.push_front(0);//{0,1,2,3}
    values.push_back(4); //{0,1,2,3,4}
    values.emplace_front(-1);//{-1,0,1,2,3,4}
    values.emplace_back(5);  //{-1,0,1,2,3,4,5}
   
    //emplace(pos,value),其中 pos 表示指明位置的迭代器,value为要插入的元素值
    values.emplace(values.end(), 6);//{-1,0,1,2,3,4,5,6}
    for (auto p = values.begin(); p != values.end(); ++p) {
        cout << *p << " ";
    }
    return 0;
}
  • list insert()成员方法
    wu
  • list splice()成员方法
    在这里插入图片描述
#include <iostream>
#include <list>
using namespace std;
int main()
{
    //创建并初始化 2 个 list 容器
    list<int> mylist1{ 1,2,3,4 }, mylist2{10,20,30};
    list<int>::iterator it = ++mylist1.begin(); //指向 mylist1 容器中的元素 2
   
    //调用第一种语法格式
    mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
                                 // mylist2:
                                 // it 迭代器仍然指向元素 2,只不过容器变为了 mylist1
    //调用第二种语法格式,将 it 指向的元素 2 移动到 mylist2.begin() 位置处
    mylist2.splice(mylist2.begin(), mylist1, it);   // mylist1: 1 10 20 30 3 4
                                                    // mylist2: 2
                                                    // it 仍然指向元素 2
   
    //调用第三种语法格式,将 [mylist1.begin(),mylist1.end())范围内的元素移动到 mylist.begin() 位置处                  
    mylist2.splice(mylist2.begin(), mylist1, mylist1.begin(), mylist1.end());//mylist1:
                                                                             //mylist2:1 10 20 30 3 4 2
   
    cout << "mylist1 包含 " << mylist1.size() << "个元素" << endl;
    cout << "mylist2 包含 " << mylist2.size() << "个元素" << endl;
    //输出 mylist2 容器中存储的数据
    cout << "mylist2:";
    for (auto iter = mylist2.begin(); iter != mylist2.end(); ++iter) {
        cout << *iter << " ";
    }
    return 0;
}
  • empty()还是size()判空哪个好?
    empty对任意的容器都是常数时间
    对于有点list实现,size需要线性时间
  • STL list删除元素详解
    在这里插入图片描述
  • pop_front()、pop_back() 和 clear() 的用法
#include <iostream>
#include <list>
using namespace std;
int main()
{
    list<int>values{ 1,2,3,4 };
   
    //删除当前容器中首个元素
    values.pop_front();//{2,3,4}
   
    //删除当前容器最后一个元素
    values.pop_back();//{2,3}
   
    //清空容器,删除容器中所有的元素
    values.clear(); //{}
   
    for (auto begin = values.begin(); begin != values.end(); ++begin)
    {
        cout << *begin << " ";
    }
    return 0;
}
  • erase() 成员函数有以下 2 种语法格式:
    iterator erase (iterator position);
    iterator erase (iterator first, iterator last);
list<int>values{ 1,2,3,4,5 };
//指向元素 1 的迭代器
auto del = values.begin();
//迭代器右移,改为指向元素 2
++del;
values.erase(del); //{1,3,4,5}

// 指定区域内所有元素
list<int>values{ 1,2,3,4,5 };
//指定删除区域的左边界
auto first = values.begin();
++first;//指向元素 2
//指向删除区域的右边界
auto last = values.end();
--last;//指向元素 5
//删除 2、3 和 4
values.erase(first, last); //1 5
  • erase() 成员函数是按照被删除元素所在的位置来执行删除操作,如果想根据元素的值来执行删除操作,可以使用 remove() 成员函数。例如:
list<char>values{'a','b','c','d'};
values.remove('c');   //a b d
  • unique() 函数也有以下 2 种语法格式:
    void unique()
    void unique(BinaryPredicate)//传入一个二元谓词函数
  • 以上 2 种格式都能实现去除 list 容器中相邻重复的元素,仅保留一份。但第 2 种格式的优势在于,我们能自定义去重的规则,例如:
#include <iostream>
#include <list>
using namespace std;
//二元谓词函数
bool demo(double first, double second)
{
    return (int(first) == int(second));
}
int main()
{
    list<double> mylist{ 1,1.2,1.2,3,4,4.5,4.6 };
    //删除相邻重复的元素,仅保留一份
    mylist.unique();//{1, 1.2, 3, 4, 4.5, 4.6}
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        cout << *it << ' ';
    cout << endl;
    //demo 为二元谓词函数,是我们自定义的去重规则
    mylist.unique(demo);
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << *it << ' ';
    return 0;
}
  • 除了以上一定谓词函数的方式,还可以使用 lamba表达式以及函数对象的方式定义。
  • 除此之外,通过将自定义的谓词函数(不限定参数个数)传给 remove_if() 成员函数,list 容器中能使谓词函数成立的元素都会被删除。举个例子:
#include <iostream>
#include <list>
using namespace std;
int main()
{
    std::list<int> mylist{ 15, 36, 7, 17, 20, 39, 4, 1 };
    //删除 mylist 容器中能够使 lamba 表达式成立的所有元素。
    mylist.remove_if([](int value) {return (value < 10); }); //{15 36 17 20 39}
    for (auto it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    return 0;
}
  • STL forward_list容器
    forward_list 是 C++ 11 新添加的一类容器,其底层实现和 list 容器一样,采用的也是链表结构,只不过 forward_list 使用的是单链表,而 list 使用的是双向链表
  • forward_list容器的创建
  • 创建一个没有任何元素的空 forward_list 容器:
    std::forward_list values;
  • 创建一个包含 n 个元素的 forward_list 容器:
    std::forward_list values(10);
  • 创建一个包含 n 个元素的 forward_list 容器,并为每个元素指定初始值。例如:
    std::forward_list values(10, 5); //默认值为5
  • 在已有 forward_list 容器的情况下,通过拷贝该容器可以创建新的 forward_list 容器。例如:
    std::forward_list value1(10);
    std::forward_list value2(value1);
  • 通过拷贝其他类型容器(或者普通数组)中指定区域内的元素,可以创建新的 forward_list 容器。例如:
    //拷贝普通数组,创建forward_list容器
    int a[] = { 1,2,3,4,5 };
    std::forward_list values(a, a+5);
    //拷贝其它类型的容器,创建forward_list容器
    std::array<int, 5>arr{ 11,12,13,14,15 };
    std::forward_listvalues(arr.begin()+2, arr.end());//拷贝arr容器中的{13,14,15}
  • forward_list容器支持的成员函数
    在这里插入图片描述
  • C++ 11 标准库还新增加了 begin() 和 end() 这 2 个函数,和 forward_list 容器包含的 begin() 和 end() 成员函数不同,标准库提供的这 2 个函数的操作对象,既可以是容器,还可以是普通数组。
  • STL关联式容器种类
    C++ STL 标准库提供了 4 种关联式容器,分别为 map、set、multimap、multiset,其各自的特点如下表所示:
    在这里插入图片描述
  • 除此之外,C++ 11 还新增了 4 种哈希容器,即 unordered_map、unordered_multimap 以及 unordered_set、unordered_multiset。严格来说,它们也属于关联式容器,但由于哈希容器底层采用的是哈希表,而不是红黑树,因此本教程将它们分开进行讲解(有关哈希容器,将放在后续章节做详细讲解)。
  • 创建C++ map容器的几种方法
  • 通过调用 map 容器类的默认构造函数,可以创建出一个空的 map 容器,比如:
    std::map<std::string, int>myMap;
  • 当然在创建 map 容器的同时,也可以进行初始化,比如:
    std::map<std::string, int>myMap{ {“C语言教程”,10},{“STL教程”,20} };
  • map 容器中存储的键值对,其本质都是 pair 类模板创建的 pair 对象。因此,下面程序也可以创建出一模一样的 myMap 容器:
    std::map<std::string, int>myMap{std::make_pair(“C语言教程”,10),std::make_pair(“STL教程”,20)};
  • 除此之外,在某些场景中,可以利用先前已创建好的 map 容器,再创建一个新的 map 容器。例如:
    std::map<std::string, int>newMap(myMap);
  • map 类模板还支持取已建 map 容器中指定区域内的键值对,创建并初始化新的 map 容器。例如:
    std::map<std::string, int>myMap{ {“C语言教程”,10},{“STL教程”,20} };
    std::map<std::string, int>newMap(++myMap.begin(), myMap.end());
  • 当然,在以上几种创建 map 容器的基础上,我们都可以手动修改 map 容器的排序规则。默认情况下,map 容器调用 std::less 规则,根据容器内各键值对的键的大小,对所有键值对做升序排序。
    std::map<std::string, int>myMap{ {“C语言教程”,10},{“STL教程”,20} };
    std::map<std::string, int, std::lessstd::string >myMap{ {“C语言教程”,10},{“STL教程”,20} };
  • 下面程序手动修改了 myMap 容器的排序规则,令其作降序排序:
    std::map<std::string, int, std::greaterstd::string >myMap{ {“C语言教程”,10},{“STL教程”,20} };
  • C++ map容器包含的成员方法
    在这里插入图片描述
  • 下面的样例演示了表 1 中部分成员方法的用法
#include <iostream>
#include <map>      // map
#include <string>       // string
using namespace std;
int main() {
    //创建空 map 容器,默认根据个键值对中键的值,对键值对做降序排序
    std::map<std::string, std::string, std::greater<std::string>>myMap;
    //调用 emplace() 方法,直接向 myMap 容器中指定位置构造新键值对
    myMap.emplace("C语言教程","http://c.biancheng.net/c/");
    myMap.emplace("Python教程", "http://c.biancheng.net/python/");
    myMap.emplace("STL教程", "http://c.biancheng.net/stl/");
    //输出当前 myMap 容器存储键值对的个数
    cout << "myMap size==" << myMap.size() << endl;
    //判断当前 myMap 容器是否为空
    if (!myMap.empty()) {
        //借助 myMap 容器迭代器,将该容器的键值对逐个输出
        for (auto i = myMap.begin(); i != myMap.end(); ++i) {
            cout << i->first << " " << i->second << endl;
        }
    }  
    return 0;
}
  • C++ STL 标准库为 map 容器配备的是双向迭代器(bidirectional iterator)。这意味着,map 容器迭代器只能进行 ++p、p++、–p、p–、*p 操作,并且迭代器之间只能使用 == 或者 != 运算符进行比较。
    在这里插入图片描述
  • 下面程序以 begin()/end() 组合为例,演示了如何遍历 map 容器:
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},{"C语言教程","http://c.biancheng.net/c/"} };
    //调用 begin()/end() 组合,遍历 map 容器
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}
  • map 类模板中还提供了 find() 成员方法,它能帮我们查找指定 key 值的键值对,如果成功找到,则返回一个指向该键值对的双向迭代器;反之,其功能和 end() 方法相同。
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    //查找键为 "Java教程" 的键值对
    auto iter = myMap.find("Java教程");
    //从 iter 开始,遍历 map 容器
    for (; iter != myMap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}
  • map 类模板中还提供有 lower_bound(key) 和 upper_bound(key) 成员方法,它们的功能是类似的,唯一的区别在于:
    lower_bound(key) 返回的是指向第一个键不小于 key 的键值对的迭代器;
    upper_bound(key) 返回的是指向第一个键大于 key 的键值对的迭代器;
  • 下面程序演示了它们的功能
#include <iostream>
#include <map>      // pair
#include <string>       // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    //找到第一个键的值不小于 "Java教程" 的键值对
    auto iter = myMap.lower_bound("Java教程");
    cout << "lower:" << iter->first << " " << iter->second << endl;
   
    //找到第一个键的值大于 "Java教程" 的键值对
    iter = myMap.upper_bound("Java教程");
    cout <<"upper:" << iter->first << " " << iter->second << endl;
    // lower:Java教程 http://c.biancheng.net/java/
    // upper:STL教程 http://c.biancheng.net/stl/
    return 0;
}
  • equal_range(key) 成员方法可以看做是 lower_bound(key) 和 upper_bound(key) 的结合体,该方法会返回一个 pair 对象,其中的 2 个元素都是迭代器类型,其中 pair.first 实际上就是 lower_bound(key) 的返回值,而 pair.second 则等同于 upper_bound(key) 的返回值。
  • equal_range(key) 成员方法表示的一个范围,位于此范围中的键值对,其键的值都为 key。举个例子:
#include <iostream>
#include <utility>  //pair
#include <map>      // map
#include <string>       // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<string, string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                   {"C语言教程","http://c.biancheng.net/c/"},
                                   {"Java教程","http://c.biancheng.net/java/"} };
    //创建一个 pair 对象,来接收 equal_range() 的返回值
    pair <std::map<string, string>::iterator, std::map<string, string>::iterator> myPair = myMap.equal_range("C语言教程");
    //通过遍历,输出 myPair 指定范围内的键值对
    for (auto iter = myPair.first; iter != myPair.second; ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}
  • map 容器的类模板中提供了以下 2 种方法,可直接获取 map 容器指定键对应的值。
  • map 类模板中对[ ]运算符进行了重载,这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值。
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    string cValue = myMap["C语言教程"];
    cout << cValue << endl;
    return 0;
}
  • 注意,只有当 map 容器中确实存有包含该指定键的键值对,借助重载的 [ ] 运算符才能成功获取该键对应的值;反之,若当前 map 容器中没有包含该指定键的键值对,则此时使用 [ ] 运算符将不再是访问容器中的元素,而变成了向该 map 容器中增添一个键值对。其中,该键值对的键用 [ ] 运算符中指定的键,其对应的值取决于 map 容器规定键值对中值的数据类型,如果是基本数据类型,则值为 0;如果是 string 类型,其值为 “”,即空字符串(即使用该类型的默认值作为键值对的值)。
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建空 map 容器
    std::map<std::string, int>myMap;
    int cValue = myMap["C语言教程"];
    for (auto i = myMap.begin(); i != myMap.end(); ++i) {
        cout << i->first << " "<< i->second << endl;
    }
    // C语言教程 0
    return 0;
}
  • 如果添加的键在map中已经存在,则会修改存在的键值,如下:
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建空 map 容器
    std::map<string, string>myMap;
    myMap["STL教程"]="http://c.biancheng.net/java/";
    myMap["Python教程"] = "http://c.biancheng.net/python/";
    myMap["STL教程"] = "http://c.biancheng.net/stl/";
    for (auto i = myMap.begin(); i != myMap.end(); ++i) {
        cout << i->first << " " << i->second << endl;
    }
    // Python教程 http://c.biancheng.net/python/
	// STL教程 http://c.biancheng.net/stl/
    return 0;
}
  • 除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    cout << myMap.at("C语言教程") << endl;
    //下面一行代码会引发 out_of_range 异常
    //cout << myMap.at("Python教程") << endl;
    return 0;
}
  • 除了可以直接获取指定键对应的值之外,还可以借助 find() 成员方法间接实现此目的。和以上 2 种方式不同的是,该方法返回的是一个迭代器,即如果查找成功,该迭代器指向查找到的键值对;反之,则指向 map 容器最后一个键值对之后的位置(和 end() 成功方法返回的迭代器一样)。
#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    map< std::string, std::string >::iterator myIter = myMap.find("C语言教程");
    cout << myIter->first << " " << myIter->second << endl; //此程序中如果 find() 查找失败,会导致此行代码运行出错。因为当 find() 方法查找失败时,其返回的迭代器指向的是容器中最后一个键值对之后的位置,即不指向任何有意义的键值对,也就没有所谓的 first 和 second 成员了。
    return 0;
}

如果以上方法都不适用,我们还可以遍历整个 map 容器,找到包含指定键的键值对,进而获取该键对应的值。比如:

#include <iostream>
#include <map>      // map
#include <string>   // string
using namespace std;
int main() {
    //创建并初始化 map 容器
    std::map<std::string, std::string>myMap{ {"STL教程","http://c.biancheng.net/stl/"},
                                             {"C语言教程","http://c.biancheng.net/c/"},
                                             {"Java教程","http://c.biancheng.net/java/"} };
    for (auto iter = myMap.begin(); iter != myMap.end(); ++iter) {
        //调用 string 类的 compare() 方法,找到一个键和指定字符串相同的键值对
        if (!iter->first.compare("C语言教程")) {
            cout << iter->first << " " << iter->second << endl;
        }
    }
    return 0;
}
  • 实际上,除了使用 [ ] 运算符实现向 map 容器中添加新键值对外,map 类模板中还提供有 insert() 成员方法,该方法专门用来向 map 容器中插入新的键值对。
    //以普通引用的方式传递 val 参数
    iterator insert (const_iterator position, const value_type& val);
    //以右值引用的方式传递 val 键值对参数
    template
    iterator insert (const_iterator position, P&& val);
  • insert() 方法还支持向当前 map 容器中插入其它 map 容器指定区域内的所有键值对,该方法的语法格式如下:
    template
    void insert (InputIterator first, InputIterator last);
  • 除了以上一种格式外,insert() 方法还允许一次向 map 容器中插入多个键值对,其语法格式为:
    void insert ({val1, val2, …});
#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;
int main()
{
    //创建空的 map 容器
    std::map<std::string, std::string>mymap;
    //向 mymap 容器中添加 3 个键值对
    mymap.insert({ {"STL教程", "http://c.biancheng.net/stl/"},
                   { "C语言教程","http://c.biancheng.net/c/" },
                   { "Java教程","http://c.biancheng.net/java/" } });
    for (auto iter = mymap.begin(); iter != mymap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}
  • 除了 insert() 方法,map 类模板还提供 emplace() 和 emplace_hint() 方法,它们也可以完成向 map 容器中插入键值对的操作,且效率还会 insert() 方法高。
  • 下面程序演示 emplace_hint() 方法的用法:
#include <iostream>
#include <map>  //map
#include <string> //string
using namespace std;
int main()
{
    //创建并初始化 map 容器
    std::map<string, string>mymap;
    //指定在 map 容器插入键值对
    map<string, string>::iterator iter = mymap.emplace_hint(mymap.begin(),"STL教程", "http://c.biancheng.net/stl/");
    cout << iter->first << " " << iter->second << endl;
    iter = mymap.emplace_hint(mymap.begin(), "C语言教程", "http://c.biancheng.net/c/");
    cout << iter->first << " " << iter->second << endl;
    //插入失败样例
    iter = mymap.emplace_hint(mymap.begin(), "STL教程", "http://c.biancheng.net/java/");
    cout << iter->first << " " << iter->second << endl;
    return 0;
}
  • 创建C++ multimap容器的方法
  • 通过调用 multimap 类模板的默认构造函数,可以创建一个空的 multimap 容器:
    std::multimap<std::string, std::string>mymultimap;
  • 在创建 multimap 容器的同时,还可以进行初始化操作
//创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };
  • 实际上,我们完全可以先手动创建好键值对,然后再用其初始化 multimap 容器。下面程序使用了 2 种方式创建 pair 类型键值对,再用其初始化 multimap 容器,它们是完全等价的
//借助 pair 类模板的构造函数来生成各个pair类型的键值对
multimap<string, string>mymultimap{
    pair<string,string>{"C语言教程", "http://c.biancheng.net/c/"},
    pair<string,string>{ "Python教程", "http://c.biancheng.net/python/"},
    pair<string,string>{ "STL教程", "http://c.biancheng.net/stl/"}
};
//调用 make_pair() 函数,生成键值对元素
//创建并初始化 multimap 容器
multimap<string, string>mymultimap{
    make_pair("C语言教程", "http://c.biancheng.net/c/"),
    make_pair("Python教程", "http://c.biancheng.net/python/"),
    make_pair("STL教程", "http://c.biancheng.net/stl/")
};
  • 除此之外,通过调用 multimap 类模板的拷贝(复制)构造函数,也可以初始化新的 multimap 容器。例如:
multimap<string, string>newmultimap(mymultimap);
  • 在 C++ 11 标准中,还为 multimap 类增添了移动构造函数。即当有临时的 multimap 容器作为参数初始化新 multimap 容器时,其底层就会调用移动构造函数来实现初始化操作。举个例子:
//创建一个会返回临时 multimap 对象的函数
multimap<string, string> dismultimap() {
    multimap<string, string>tempmultimap{ {"C语言教程", "http://c.biancheng.net/c/"},{"Python教程", "http://c.biancheng.net/python/"} };
    return tempmultimap;
}  
//调用 multimap 类模板的移动构造函数创建 newMultimap 容器
multimap<string, string>newmultimap(dismultimap());
  • multimap 类模板还支持从已有 multimap 容器中,选定某块区域内的所有键值对,用作初始化新 multimap 容器时使用。例如:
//创建并初始化 multimap 容器
multimap<string, string>mymultimap{ {"C语言教程", "http://c.biancheng.net/c/"},
                                    {"Python教程", "http://c.biancheng.net/python/"},
                                    {"STL教程", "http://c.biancheng.net/stl/"} };
multimap<string, string>newmultimap(++mymultimap.begin(), mymultimap.end());
  • multimap 类模板共可以接收 4 个参数,其中第 3 个参数可用来修改 multimap 容器内部的排序规则。默认情况下,此参数的值为std::less,这意味着以下 2 种创建 multimap 容器的方式是等价的:
multimap<char, int>mymultimap{ {'a',1},{'b',2} };
multimap<char, int, std::less<char>>mymultimap{ {'a',1},{'b',2} };
/* 顺序为
   <a,1>
   <b,2>*/
multimap<char, int, std::greater<char>>mymultimap{ {'a',1},{'b',2} };
/* 顺序为
   <b,2>
   <a,1>*/
  • C++ multimap容器包含的成员方法
    在这里插入图片描述
  • 下面例子演示了表 1 中部分成员方法的用法:
#include <iostream>
#include <map>  //map
using namespace std;   
int main()
{
    //创建并初始化 multimap 容器
    multimap<char, int>mymultimap{ {'a',10},{'b',20},{'b',15}, {'c',30} };
    //输出 mymultimap 容器存储键值对的数量
    cout << mymultimap.size() << endl;
    //输出 mymultimap 容器中存储键为 'b' 的键值对的数量
    cout << mymultimap.count('b') << endl;
    for (auto iter = mymultimap.begin(); iter != mymultimap.end(); ++iter) {
        cout << iter->first << " " << iter->second << endl;
    }
    return 0;
}
  • STL set容器
    和 map、multimap 容器不同,使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
    举个例子,如下有 2 组键值对数据:
    {<‘a’, 1>, <‘b’, 2>, <‘c’, 3>}
    {<‘a’, ‘a’>, <‘b’, ‘b’>, <‘c’, ‘c’>}
    显然,第一组数据中各键值对的键和值不相等,而第二组中各键值对的键和值对应相等。对于 set 容器来说,只能存储第 2 组键值对,而无法存储第一组键值对。
  • 基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。仍以存储上面第 2 组键值对为例,只需要为 set 容器提供 {‘a’,‘b’,‘c’} ,该容器即可成功将它们存储起来。
  • map、multimap 容器都会自行根据键的大小对存储的键值对进行排序,set 容器也会如此,只不过 set 容器中各键值对的键 key 和值 value 是相等的,根据 key 排序,也就等价为根据 value 排序
  • 使用 set 容器存储的各个元素的值必须各不相同。更重要的是,从语法上讲 set 容器并没有强制对存储元素的类型做 const 修饰,即 set 容器中存储的元素的值是可以修改的。但是,C++ 标准为了防止用户修改容器中元素的值,对所有可能会实现此操作的行为做了限制,使得在正常情况下,用户是无法做到修改 set 容器中元素的值的。
  • 最正确的修改 set 容器中元素值的做法是:先删除该元素,然后再添加一个修改后的元素
  • set容器的几种方法
  • 调用默认构造函数,创建空的 set 容器。比如:
std::set<std::string> myset;
  • 除此之外,set 类模板还支持在创建 set 容器的同时,对其进行初始化。例如:
std::set<std::string> myset{"http://c.biancheng.net/java/",
                            "http://c.biancheng.net/stl/",
                            "http://c.biancheng.net/python/"};
  • set 类模板中还提供了拷贝(复制)构造函数,可以实现在创建新 set 容器的同时,将已有 set 容器中存储的所有元素全部复制到新 set 容器中。
std::set<std::string> copyset(myset);
//等同于
//std::set<std::string> copyset = myset
  • C++ 11 标准还为 set 类模板新增了移动构造函数,其功能是实现创建新 set 容器的同时,利用临时的 set 容器为其初始化。比如:
set<string> retSet() {
    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                            "http://c.biancheng.net/stl/",
                            "http://c.biancheng.net/python/" };
    return myset;
}
std::set<std::string> copyset(retSet());
//或者
//std::set<std::string> copyset = retSet();
  • 在第 3 种方式的基础上,set 类模板还支持取已有 set 容器中的部分元素,来初始化新 set 容器。例如:
std::set<std::string> myset{ "http://c.biancheng.net/java/",
                    "http://c.biancheng.net/stl/",
                    "http://c.biancheng.net/python/" };
std::set<std::string> copyset(++myset.begin(), myset.end());
  • 以上几种方式创建的 set 容器,都采用了默认的std::less规则。其实,借助 set 类模板定义中第 2 个参数,我们完全可以手动修改 set 容器中的排序规则。比如:
std::set<std::string,std::greater<string> > myset{
    "http://c.biancheng.net/java/",
    "http://c.biancheng.net/stl/",
    "http://c.biancheng.net/python/"};
  • STL set容器包含的成员方法
    在这里插入图片描述
  • 下面程序演示了表 1 中部分成员函数的用法:
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建空set容器
    std::set<std::string> myset;
    //空set容器不存储任何元素
    cout << "1、myset size = " << myset.size() << endl;
    //向myset容器中插入新元素
    myset.insert("http://c.biancheng.net/java/");
    myset.insert("http://c.biancheng.net/stl/");
    myset.insert("http://c.biancheng.net/python/");
    cout << "2、myset size = " << myset.size() << endl;
    //利用双向迭代器,遍历myset
    for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}
  • STL set容器迭代器用法详解

  • 和 map 容器不同,C++ STL 中的 set 容器类模板中未提供 at() 成员函数,也未对 [] 运算符进行重载。因此,要想访问 set 容器中存储的元素,只能借助 set 容器的迭代器。

  • C++ STL 标准库为 set 容器配置的迭代器类型为双向迭代器
    在这里插入图片描述

  • 下面程序以 begin()/end() 为例,演示了如何使用图 2 中相关迭代器遍历 set 容器:

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化set容器
    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                                 "http://c.biancheng.net/stl/",
                                 "http://c.biancheng.net/python/"
    };
    //利用双向迭代器,遍历myset
    for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}
  • 只要给定目标元素的值,insert() 方法即可将该元素添加到 set 容器中,其语法格式如下:
    //普通引用方式传参
    pair<iterator,bool> insert (const value_type& val);
    //右值引用方式传参
    pair<iterator,bool> insert (value_type&& val);
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化set容器
    std::set<std::string> myset;
    //准备接受 insert() 的返回值
    pair<set<string>::iterator, bool> retpair;
    //采用普通引用传值方式
    string str = "http://c.biancheng.net/stl/";
    retpair = myset.insert(str);
    cout << "iter->" << *(retpair.first) << " " << "bool = " << retpair.second << endl;
    //采用右值引用传值方式
    retpair = myset.insert("http://c.biancheng.net/python/");
    cout << "iter->" << *(retpair.first) << " " << "bool = " << retpair.second << endl;
    return 0;
}
  • insert() 还可以指定将新元素插入到 set 容器中的具体位置,其语法格式如下:
    //以普通引用的方式传递 val 值
    iterator insert (const_iterator position, const value_type& val);
    //以右值引用的方式传递 val 值
    iterator insert (const_iterator position, value_type&& val);
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化set容器
    std::set<std::string> myset;
    //准备接受 insert() 的返回值
    set<string>::iterator iter;
    //采用普通引用传值方式
    string str = "http://c.biancheng.net/stl/";
    iter = myset.insert(myset.begin(),str);
    cout << "myset size =" << myset.size() << endl;
    //采用右值引用传值方式
    iter = myset.insert(myset.end(),"http://c.biancheng.net/python/");
    cout << "myset size =" << myset.size() << endl;
    return 0;
}
  • 注意,使用 insert() 方法将目标元素插入到 set 容器指定位置后,如果该元素破坏了容器内部的有序状态,set 容器还会自行对新元素的位置做进一步调整。也就是说,insert() 方法中指定新元素插入的位置,并不一定就是该元素最终所处的位置。
  • emplace() 和 emplace_hint() 是 C++ 11 标准加入到 set 类模板中的,相比具有同样功能的 insert() 方法,完成同样的任务,emplace() 和 emplace_hint() 的效率会更高。
  • emplace() 方法的语法格式如下:
    template <class… Args>
    pair<iterator,bool> emplace (Args&&… args);
  • 下面程序演示 emplace() 方法的具体用法:
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化 set 容器
    std::set<string>myset;
    //向 myset 容器中添加元素
    pair<set<string, string>::iterator, bool> ret = myset.emplace("http://c.biancheng.net/stl/");
    cout << "myset size = " << myset.size() << endl;
    cout << "ret.iter = <" << *(ret.first) << ", " << ret.second << ">" << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值