C++STL: set and multiset of associative containers

1. set

overview

The map container and multimap container were introduced earlier. Similarly, the C++ STL standard library also provides two containers, set and multiset, which also belong to associative containers.

Different from map and multimap containers, each key-value pair stored in the set container requires that the key key and the value value must be equal. For example, there are 2 sets of key-value pair data as follows:

{<'a', 1>, <'b', 2>, <'c', 3>}
{<'a', 'a'>, <'b', 'b'>, <'c', 'c'>}

Obviously, the keys and values ​​of each key-value pair in the first set of data are not equal, while the keys and values ​​of each key-value pair in the second set are equal. For the set container, only the second set of key-value pairs can be stored, but the first set of key-value pairs cannot be stored.

Based on this feature of the set container, when using the set container to store key-value pairs, you only need to provide it with the value of each key-value pair (that is, the value of the key). Still taking the storage of the second group of key-value pairs above as an example, you only need to provide {'a','b','c'} for the set container, and the container can successfully store them.

Through the previous study, we know that the map and multimap containers will sort the stored key-value pairs according to the size of the key, and the set container will also do the same, except that the key and value of each key-value pair in the set container are equal , sorting by key is equivalent to sorting by value.

Also, the values ​​of each element stored using a set container must be distinct. More importantly, from a grammatical point of view, the set container does not force the type of the storage element to be const modified, that is, the value of the element stored in the set container can be modified. However, in order to prevent the user from modifying the value of the elements in the container, the C++ standard restricts all possible behaviors that can achieve this operation, so that under normal circumstances, the user cannot modify the value of the elements in the set container.

For beginners, do not try to directly modify the value of the stored elements in the set container, which is likely to destroy the order of the elements in the set container. The most correct way to modify the value of the elements in the set container is: delete the element first element, and then add a modified element.

The set container is defined in the <set> header file and resides in the std namespace. Therefore, if you want to use the set container in the program, the program code should first contain the following statement:

#include <set>
using namespace std;

Note that the second line of code is not necessary. If not, you need to manually indicate the std namespace when using the set container in subsequent programs (recommended for beginners).

The class template for the set container is defined as follows:

template < class T,                        // 键 key 和值 value 的类型
           class Compare = less<T>,        // 指定 set 容器内部的排序规则
           class Alloc = allocator<T>      // 指定分配器对象的类型
           > class set;

Note that since each key-value pair stored in the set container has exactly the same key and value, which means they have the same type, so in the definition of the set container class template, only the first parameter is used to set the stored data type.

For the 3 parameters in the set class template, the last 2 parameters have their own default values, and only the first 2 parameters are needed in almost all scenarios, and the third parameter will not be used.

member function

The following table lists the common member methods provided by the set container and their respective functions:

member method Function
begin() Returns a bidirectional iterator pointing to the first (note, the sorted first) element in the container. If the set container is const-qualified, this method returns a bidirectional iterator of const type.
end() Returns a bidirectional iterator pointing to the position after the last element of the container (note that it is the last one that has been sorted), usually used in conjunction with begin(). If the set container is const-qualified, this method returns a bidirectional iterator of const type.
rbegin() Returns a reverse bidirectional iterator pointing to the last (note, the last sorted) element. If the set container is const-qualified, this method returns a reverse bidirectional iterator of const type.
rend() Returns a reverse bidirectional iterator pointing to the position preceding the position of the first (note, the first sorted) element. If the set container is const-qualified, this method returns a reverse bidirectional iterator of const type.
cbegin() It has the same function as begin(), except that the const attribute is added on top of it, which cannot be used to modify the element value stored in the container.
a few() It has the same function as end(), but on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
crbegin() It has the same function as rbegin(), but on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
crend() It has the same function as rend(), except that on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
find(val) Look for the element whose value is val in the set container, if found successfully, return the bidirectional iterator pointing to the element; otherwise, return the same iterator as the end() method. In addition, if the set container is const-qualified, this method returns a bidirectional iterator of const type.
lower_bound(val) Returns a bidirectional iterator pointing to the first element greater than or equal to val in the current set container. If the set container is const-qualified, this method returns a bidirectional iterator of const type.
upper_bound(val) Returns an iterator pointing to the first element greater than val in the current set container. If the set container is const-qualified, this method returns a bidirectional iterator of const type.
equal_range(val) This method returns a pair object (contains two bidirectional iterators), where the return values ​​of pair.first and lower_bound() methods are equivalent, and the return values ​​of pair.second and upper_bound() methods are equivalent. That is, the method will return a range that contains elements with the value val (each element in the set container is unique, so the range contains at most one element).
empty() Returns true if the container is empty; otherwise, false.
size() Returns the number of elements stored in the current set container.
max_size() Returns the maximum number of elements that the set container can hold. Different operating systems have different return values.
insert() Insert elements into the set container.
erase() Deletes the elements stored in the set container.
swap() Swaps all elements stored in 2 set containers. This means that the 2 set containers operated on must be of the same type.
clear() Clear all the elements in the set container, that is, set the size() of the set container to 0.
place() Constructs a new element directly at the specified position in the current set container. Its effect is the same as insert(), but more efficient.
emplace_hint() In essence, it is the same as the way emplace() constructs new elements in the set container. The difference is that the user must provide this method with an iterator indicating the position where the new element is generated, and it is the first element of the method. parameter.
count(val) In the current set container, find the number of elements whose value is val, and return it. Note that since the value of each element in the set container is unique, the maximum return value of this function is 1.

Several ways to create C++ set container

Common methods for creating set containers generally include the following five methods.

  1. Call the default constructor to create an empty set container. for example:
std::set<std::string> myset;

Thus, a set container is created, which adopts the default std::less<T>rules and sorts the stored string type elements in ascending order. Note that since the set container supports adding new elements to the interior at any time, the method of creating an empty set container is often used.

  1. In addition, the set class template also supports initializing the set container while creating it. For example:
std::set<std::string> myset{"http://c.biancheng.net/java/",
                            "http://c.biancheng.net/stl/",
                            "http://c.biancheng.net/python/"};

Thus, the myset container containing 3 string elements is created. Since it adopts the default std::less<T>rule, the order of its internal storage string elements is as follows:

"http://c.biancheng.net/java/"
"http://c.biancheng.net/python/"
"http://c.biancheng.net/stl/"
  1. The set class template also provides a copy (replication) constructor, which can copy all the elements stored in the existing set container to the new set container while creating a new set container.

For example, based on the myset container created in the second way, execute the following code:

std::set<std::string> copyset(myset);
// 等同于
//std::set<std::string> copyset = myset

On the basis of creating the copyset container, this line of code will also copy all the elements stored in the myset container to the copyset container.

In addition, the C++ 11 standard also adds a move constructor to the set class template. Its function is to create a new set container and initialize it with a temporary set container. for example:

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();

Note that since the return value of the retSet() function is a temporary set container, when the copyset container is initialized, the move constructor in the set class template is called instead of the copy constructor.

Obviously, whether calling the copy constructor or calling the copy constructor, it is necessary to ensure that the types of the two containers are exactly the same.

  1. On the basis of the third method, the set class template also supports taking some elements in the existing set container to initialize the new set container. For example:
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());

The copyset container thus initialized contains only the following two strings:

"http://c.biancheng.net/python/"
"http://c.biancheng.net/stl/"
  1. The set containers created in the above ways all adopt the default std::less<T>rules. In fact, with the help of the second parameter in the template definition of the set class, we can manually modify the sorting rules in the set container. for example:
std::set<std::string,std::greater<string> > myset{
    "http://c.biancheng.net/java/",
    "http://c.biancheng.net/stl/",
    "http://c.biancheng.net/python/"};

By selecting the descending order of std::greater<string>, the storage order of elements in the myset container is:

"http://c.biancheng.net/stl/"
"http://c.biancheng.net/python/"
"http://c.biancheng.net/java/"

iterator

Unlike the map container, the at() member function is not provided in the set container class template in C++ STL, nor is the [] operator overloaded. Therefore, if you want to access the elements stored in the set container, you can only use the iterator of the set container.

It is worth mentioning that the iterator type configured by the C++ STL standard library for the set container is a bidirectional iterator. This means, assuming that p is an iterator of this type, it can only be operated ++p、p++、--p、p--、*p, and the comparison between two bidirectional iterators can only use ==the or !=operator.

Among all the member functions provided by the set container class template, the member functions that return iterators are shown in the following table:

member method Function
begin() Returns a bidirectional iterator pointing to the first (note, the sorted first) element in the container. If the set container is const-qualified, this method returns a bidirectional iterator of const type.
end() Returns a bidirectional iterator pointing to the position after the last element of the container (note that it is the last one that has been sorted), usually used in conjunction with begin(). If the set container is const-qualified, this method returns a bidirectional iterator of const type.
rbegin() Returns a reverse bidirectional iterator pointing to the last (note, the last sorted) element. If the set container is const-qualified, this method returns a reverse bidirectional iterator of const type.
rend() Returns a reverse bidirectional iterator pointing to the position preceding the position of the first (note, the first sorted) element. Usually used in conjunction with rbegin(). If the set container is const-qualified, this method returns a reverse bidirectional iterator of const type.
cbegin() It has the same function as begin(), except that the const attribute is added on top of it, which cannot be used to modify the element value stored in the container.
a few() It has the same function as end(), but on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
crbegin() It has the same function as rbegin(), but on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
crend() It has the same function as rend(), except that on the basis of it, a const attribute is added, which cannot be used to modify the element value stored in the container.
find(val) Look for the element whose value is val in the set container, if found successfully, return the bidirectional iterator pointing to the element; otherwise, return the same iterator as the end() method. In addition, if the set container is const-qualified, this method returns a bidirectional iterator of const type.
lower_bound(val) Returns a bidirectional iterator pointing to the first element greater than or equal to val in the current set container. If the set container is const-qualified, this method returns a bidirectional iterator of const type.
upper_bound(val) 返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。

注意,以上成员函数返回的迭代器,指向的只是 set 容器中存储的元素,而不再是键值对。另外,以上成员方法返回的迭代器,无论是 const 类型还是非 const 类型,都不能用于修改 set 容器中的值。

  1. 下面程序以 begin()/end() 为例,演示了如何使用相关迭代器遍历 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;
}

程序执行结果为:

http://c.biancheng.net/java/
http://c.biancheng.net/python/
http://c.biancheng.net/stl/

再次强调,正如程序第 15 行代码所示的那样,因为 iter 迭代器指向的是 set 容器存储的某个元素,而不是键值对,因此通过 *iter 可以直接获取该迭代器指向的元素的值。

  1. 除此之外,如果只想遍历 set 容器中指定区域内的部分数据,则可以借助 find()、lower_bound() 以及 upper_bound() 实现。通过调用它们,可以获取一个指向指定元素的迭代器。

需要特别指出的是,equal_range(val) 函数的返回值是一个 pair 类型数据,其包含 2 个迭代器,表示 set 容器中和指定参数 val 相等的元素所在的区域,但由于 set 容器中存储的元素各不相等,因此该函数返回的这 2 个迭代器所表示的范围中,最多只会包含 1 个元素。

举个例子:

#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/"
    };
   
    set<string>::iterator iter = myset.find("http://c.biancheng.net/python/");
    for (;iter != myset.end();++iter)
    {
        cout << *iter << endl;
    }
    return 0;
}

程序执行结果为:

http://c.biancheng.net/python/
http://c.biancheng.net/stl/

值得一提的是,虽然 C++ STL 标准中,set 类模板中包含 lower_bound()、upper_bound()、equal_range() 这 3 个成员函数,但它们更适用于 multiset 容器,几乎不会用于操作 set 容器。

set insert()方法详解

如果想向 set 容器中继续添加元素,可以借助 set 类模板提供的 insert() 方法。为满足不同场景的需要,C++ 11 标准的 set 类模板中提供了多种不同语法格式的 insert() 成员方法,它们各自的功能和用法如下所示。

  1. 只要给定目标元素的值,insert() 方法即可将该元素添加到 set 容器中,其语法格式如下:
// 普通引用方式传参
pair<iterator,bool> insert (const value_type& val);
// 右值引用方式传参
pair<iterator,bool> insert (value_type&& val);

其中,val 表示要添加的新元素,该方法的返回值为 pair 类型。

以上 2 种格式的区别仅在于传递参数的方式不同,即第一种采用普通引用的方式传参,而第二种采用右值引用的方式传参。右值引用为 C++ 11 新添加的一种引用方式。

可以看到,以上 2 种语法格式的 insert() 方法,返回的都是 pair 类型的值,其包含 2 个数据,一个迭代器和一个 bool 值:

  • 当向 set 容器添加元素成功时,该迭代器指向 set 容器新添加的元素,bool 类型的值为 true;
  • 如果添加失败,即证明原 set 容器中已存有相同的元素,此时返回的迭代器就指向容器中相同的此元素,同时 bool 类型的值为 false。

举个例子:

#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;
}

程序执行结果为:

iter->http://c.biancheng.net/stl/ bool = 1
iter->http://c.biancheng.net/python/ bool = 1

通过观察输出结果不难看出,程序中两次借助 insert() 方法向 set 容器中添加元素,都成功了。

  1. insert() 还可以指定将新元素插入到 set 容器中的具体位置,其语法格式如下:
// 以普通引用的方式传递 val 值
iterator insert (const_iterator position, const value_type& val);
// 以右值引用的方式传递 val 值
iterator insert (const_iterator position, value_type&& val);

以上 2 种语法格式中,insert() 函数的返回值为迭代器:

  • 当向 set 容器添加元素成功时,该迭代器指向容器中新添加的元素;
  • 当添加失败时,证明原 set 容器中已有相同的元素,该迭代器就指向 set 容器中相同的这个元素。

举个例子:

#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;
}

程序执行结果为:

myset size =1
myset size =2

注意,使用 insert() 方法将目标元素插入到 set 容器指定位置后,如果该元素破坏了容器内部的有序状态,set 容器还会自行对新元素的位置做进一步调整。也就是说,insert() 方法中指定新元素插入的位置,并不一定就是该元素最终所处的位置。

  1. insert() 方法支持向当前 set 容器中插入其它 set 容器指定区域内的所有元素,只要这 2 个 set 容器存储的元素类型相同即可。insert() 方法的语法格式如下:
template <class InputIterator>
 void insert (InputIterator first, InputIterator last);

其中 first 和 last 都是迭代器,它们的组合 [first,last) 可以表示另一 set 容器中的一块区域,该区域包括 first 迭代器指向的元素,但不包含 last 迭代器指向的元素。举个例子:

#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 创建并初始化set容器
    std::set<std::string> myset{ "http://c.biancheng.net/stl/",
                                "http://c.biancheng.net/python/",
                                "http://c.biancheng.net/java/" };
    // 创建一个同类型的空 set 容器
    std::set<std::string> otherset;
    // 利用 myset 初始化 otherset
    otherset.insert(++myset.begin(), myset.end());
    // 输出 otherset 容器中的元素
    for (auto iter = otherset.begin(); iter != otherset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}

程序执行结果为:

http://c.biancheng.net/python/
http://c.biancheng.net/stl/

注意,程序第 15 行在初始化 otherset 容器时,选取的是 myset 容器中从第 2 个元素开始(包括此元素)直到容器末尾范围内的所有元素,所以程序输出结果中只有 2 个字符串。

  1. 采用如下格式的 insert() 方法,可实现一次向 set 容器中添加多个元素:
void insert ( {E1, E2,...,En} );

其中,Ei 表示新添加的元素。举个例子:

#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 创建并初始化set容器
    std::set<std::string> myset;
    // 向 myset 中添加多个元素
    myset.insert({ "http://c.biancheng.net/stl/",
        "http://c.biancheng.net/python/",
        "http://c.biancheng.net/java/" });
    for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}

程序执行结果为:

http://c.biancheng.net/java/
http://c.biancheng.net/python/
http://c.biancheng.net/stl/

以上即为 set 类模板中 insert() 成员方法的全部用法。另外,C++ 11 标准的 set 类模板中,还提供有另外 2 个成员方法,分别为 implace() 和 implace_hint() 方法,借助它们不但能实现向 set 容器添加新元素的功能,其实现效率也比 insert() 成员方法更高。

set emplace()和emplace_hint()方法详解

set 类模板提供的所有成员方法中,能实现向指定 set 容器中添加新元素的,只有 3 个成员方法,分别为 insert()、emplace() 和 emplace_hint()。其中 insert() 成员方法的用法已在上节做了详细的介绍,本节重点介绍剩下的这 2 个成员方法。

emplace() 和 emplace_hint() 是 C++ 11 标准加入到 set 类模板中的,相比具有同样功能的 insert() 方法,完成同样的任务,emplace() 和 emplace_hint() 的效率会更高。

  1. emplace() 方法的语法格式如下:
template <class... Args>
 pair<iterator,bool> emplace (Args&&... args);

其中,参数 (Args&&… args) 指的是,只需要传入构建新元素所需的数据即可,该方法可以自行利用这些数据构建出要添加的元素。比如,若 set 容器中存储的元素类型为自定义的结构体或者类,则在使用 emplace() 方法向容器中添加新元素时,构造新结构体变量(或者类对象)需要多少个数据,就需要为该方法传入相应个数的数据。

另外,该方法的返回值类型为 pair 类型,其包含 2 个元素,一个迭代器和一个 bool 值:

  • 当该方法将目标元素成功添加到 set 容器中时,其返回的迭代器指向新插入的元素,同时 bool 值为 true;
  • 当添加失败时,则表明原 set 容器中已存在相同值的元素,此时返回的迭代器指向容器中具有相同键的这个元素,同时 bool 值为 false。

下面程序演示 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;
}

程序执行结果为:

myset size = 1
ret.iter = <http://c.biancheng.net/stl/, 1>

显然,从执行结果可以看出,通过调用 emplace() 方法,成功向空 myset 容器中添加了一个元素,并且该方法的返回值中就包含指向新添加元素的迭代器。

  1. emplace_hint() 方法的功能和 emplace() 类似,其语法格式如下:
template <class... Args>
 iterator emplace_hint (const_iterator position, Args&&... args);

和 emplace() 方法相比,有以下 2 点不同:

  • 该方法需要额外传入一个迭代器,用来指明新元素添加到 set 容器的具体位置(新元素会添加到该迭代器指向元素的前面);
  • 返回值是一个迭代器,而不再是 pair 对象。当成功添加元素时,返回的迭代器指向新添加的元素;反之,如果添加失败,则迭代器就指向 set 容器和要添加元素的值相同的元素。

下面程序演示 emplace_hint() 方法的用法:

#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 创建并初始化 set 容器
    std::set<string>myset;
    // 在 set 容器的指定位置添加键值对
    set<string>::iterator iter = myset.emplace_hint(myset.begin(), "http://c.biancheng.net/stl/");
    cout << "myset size = " << myset.size() << endl;
    cout << *iter << endl;
    return 0;
}

程序执行结果为:

myset size = 1
http://c.biancheng.net/stl/

注意,和 insert() 方法一样,虽然 emplace_hint() 方法中指定了添加新元素的位置,但 set 容器为了保持数据的有序状态,可能会移动其位置。

以上内容介绍了 emplace() 和 emplace_hint() 的用法,至于比 insert() 执行效率高的原因,可参照 map 容器 emplace() 和 emplace_hint() 比 insert() 效率高的原因,它们是完全一样的。

set删除数据:erase()和clear()方法

如果想删除 set 容器存储的元素,可以选择用 erase() 或者 clear() 成员方法。

  1. set 类模板中,erase() 方法有 3 种语法格式,分别如下:
// 删除 set 容器中值为 val 的元素
size_type erase (const value_type& val);
// 删除 position 迭代器指向的元素
iterator erase (const_iterator position);
// 删除 [first,last) 区间内的所有元素
iterator erase (const_iterator first, const_iterator last);

其中,第 1 种格式的 erase() 方法,其返回值为一个整数,表示成功删除的元素个数;后 2 种格式的 erase() 方法,返回值都是迭代器,其指向的是 set 容器中删除元素之后的第一个元素。

注意,如果要删除的元素就是 set 容器最后一个元素,则 erase() 方法返回的迭代器就指向新 set 容器中最后一个元素之后的位置(等价于 end() 方法返回的迭代器)。

下面程序演示了以上 3 种 erase() 方法的用法:

#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 创建并初始化 set 容器
    std::set<int>myset{1,2,3,4,5};
    cout << "myset size = " << myset.size() << endl;
   
    //1) 调用第一种格式的 erase() 方法
    int num = myset.erase(2); // 删除元素 2,myset={1,3,4,5}
    cout << "1、myset size = " << myset.size() << endl;
    cout << "num = " << num << endl;
    //2) 调用第二种格式的 erase() 方法
    set<int>::iterator iter = myset.erase(myset.begin()); // 删除元素 1,myset={3,4,5}
    cout << "2、myset size = " << myset.size() << endl;
    cout << "iter->" << *iter << endl;
    //3) 调用第三种格式的 erase() 方法
    set<int>::iterator iter2 = myset.erase(myset.begin(), --myset.end()); // 删除元素 3,4,myset={5}
    cout << "3、myset size = " << myset.size() << endl;
    cout << "iter2->" << *iter2 << endl;
    return 0;
}

程序执行结果为:

myset size = 5
1、myset size = 4
num = 1
2、myset size = 3
iter->3
3、myset size = 1
iter2->5
  1. 如果需要删除 set 容器中存储的所有元素,可以使用 clear() 成员方法。该方法的语法格式如下:
void clear();

显然,该方法不需要传入任何参数,也没有任何返回值。举个例子:

#include <iostream>
#include <set>
#include <string>
using namespace std;

int main()
{
    // 创建并初始化 set 容器
    std::set<int>myset{1,2,3,4,5};
    cout << "1、myset size = " << myset.size() << endl;
    // 清空 myset 容器
    myset.clear();
    cout << "2、myset size = " << myset.size() << endl;
    return 0;
}

程序执行结果为:

1、myset size = 5
2、myset size = 0

2. multiset

概述

前面对 set 容器做了详细的介绍。回忆一下,set 容器具有以下几个特性:

  • 不再以键值对的方式存储数据,因为 set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value);
  • set 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
  • 存储到 set 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的;
  • set 容器存储的元素必须互不相等。

在此基础上,C++ STL 标准库中还提供有一个和 set 容器相似的关联式容器,即 multiset 容器。所谓“相似”,是指 multiset 容器遵循 set 容器的前 3 个特性,仅在第 4 条特性上有差异。和 set 容器不同的是,multiset 容器可以存储多个值相同的元素。

也就是说,multiset 容器和 set 容器唯一的差别在于,multiset 容器允许存储多个值相同的元素,而 set 容器中只能存储互不相同的元素。

和 set 类模板一样,multiset 类模板也定义在<set>头文件,并位于 std 命名空间中。这意味着,如果想在程序中使用 multiset 容器,该程序代码应包含如下语句:

#include <set>
using namespace std;

注意,第二行代码不是必需的,如果不用,则后续程序中在使用 multiset容器时,需手动注明 std 命名空间(建议初学者使用)。

multiset 容器类模板的定义如下所示:

template < class T,                        // 存储元素的类型
           class Compare = less<T>,        // 指定容器内部的排序规则
           class Alloc = allocator<T> >    // 指定分配器对象的类型
           > class multiset;

显然,multiset 类模板有 3 个参数,其中后 2 个参数自带有默认值。值得一提的是,在实际使用中,我们最多只需要使用前 2 个参数即可,第 3 个参数不会用到。

成员函数

multiset 容器提供的成员方法,和 set 容器提供的完全一样,如下表所示:

成员方法 功能
begin() 返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
find(val) 在 multiset 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(val) 返回一个指向当前 multiset 容器中第一个大于或等于 val 的元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val) 返回一个指向当前 multiset 容器中第一个大于 val 的元素的迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含所有值为 val 的元素。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前 multiset 容器中存有元素的个数。
max_size() 返回 multiset 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
insert() 向 multiset 容器中插入元素。
erase() 删除 multiset 容器中存储的指定元素。
swap() 交换 2 个 multiset 容器中存储的所有元素。这意味着,操作的 2 个 multiset 容器的类型必须相同。
clear() 清空 multiset 容器中所有的元素,即令 multiset 容器的 size() 为 0。
emplace() 在当前 multiset 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。
emplace_hint() 本质上和 emplace() 在 multiset 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。
count(val) 在当前 multiset 容器中,查找值为 val 的元素的个数,并返回。

注意,虽然 multiset 容器和 set 容器拥有的成员方法完全相同,但由于 multiset 容器允许存储多个值相同的元素,因此诸如 count()、find()、lower_bound()、upper_bound()、equal_range()等方法,更常用于 multiset 容器。

创建C++ multiset容器的方法

创建 multiset 容器,无疑需要调用 multiset 类模板中的构造函数。值得一提的是,multiset 类模板提供的构造函数,和 set 类模板中提供创建 set 容器的构造函数,是完全相同的。这意味着,创建 set 容器的方式,也同样适用于创建 multiset 容器。

multiset 类模板中提供了 5 种构造函数,也就代表有 5 种创建 multiset 容器的方式,分别如下。

  1. 调用默认构造函数,创建空的 multiset 容器。比如:
std::multiset<std::string> mymultiset;

由此就创建好了一个 mymultiset 容器,该容器采用默认的std::less<T>规则,会对存储的 string 类型元素做升序排序。

注意,由于 multiset 容器支持随时向内部添加新的元素,因此创建空 multiset 容器的方法比较常用。

2)除此之外,multiset 类模板还支持在创建 multiset 容器的同时,对其进行初始化。例如:

std::multiset<std::string> mymultiset{ "http://c.biancheng.net/java/",
                                       "http://c.biancheng.net/stl/",
                                       "http://c.biancheng.net/python/" };

由此即创建好了包含 3 个 string 元素的 mymultiset 容器。由于其采用默认的std::less<T>规则,因此其内部存储 string 元素的顺序如下所示:

"http://c.biancheng.net/java/"
"http://c.biancheng.net/python/"
"http://c.biancheng.net/stl/"
  1. multiset 类模板中还提供了拷贝(复制)构造函数,可以实现在创建新 multiset 容器的同时,将已有 multiset 容器中存储的所有元素全部复制到新 multiset 容器中。

例如,在第 2 种方式创建的 mymultiset 容器的基础上,执行如下代码:

std::multiset<std::string> copymultiset(mymultiset);
// 等同于
//std::multiset<std::string> copymultiset = mymultiset;

该行代码在创建 copymultiset 容器的基础上,还会将 mymultiset 容器中存储的所有元素,全部复制给 copymultiset 容器一份。

另外,C++ 11 标准还为 multiset 类模板新增了移动构造函数,其功能是实现创建新 multiset 容器的同时,利用临时的 multiset 容器为其初始化。比如:

multiset<string> retMultiset() {
    std::multiset<std::string> tempmultiset{ "http://c.biancheng.net/java/",
                            "http://c.biancheng.net/stl/",
                            "http://c.biancheng.net/python/" };
    return tempmultiset;
}
std::multiset<std::string> copymultiset(retMultiset());
// 等同于
//std::multiset<std::string> copymultiset = retMultiset();

注意,由于 retMultiset() 函数的返回值是一个临时 multiset 容器,因此在初始化 copymultiset 容器时,其内部调用的是 multiset 类模板中的移动构造函数,而非拷贝构造函数。

显然,无论是调用复制构造函数还是调用拷贝构造函数,都必须保证这 2 个容器的类型完全一致。

  1. 在第 3 种方式的基础上,multiset 类模板还支持取已有 multiset 容器中的部分元素,来初始化新 multiset 容器。例如:
std::multiset<std::string> mymultiset{ "http://c.biancheng.net/java/",
                                       "http://c.biancheng.net/stl/",
                                       "http://c.biancheng.net/python/" };
std::set<std::string> copymultiset(++mymultiset.begin(), mymultiset.end());

以上初始化的 copyset 容器,其内部仅存有如下 2 个 string 字符串:

"http://c.biancheng.net/python/"
"http://c.biancheng.net/stl/"
  1. 以上几种方式创建的 multiset 容器,都采用了默认的std::less<T>规则。其实,借助 multiset 类模板定义中的第 2 个参数,我们完全可以手动修改 multiset 容器中的排序规则。

下面样例中,使用了 STL 标准库提供的 std::greater<T> 排序方法,作为 multiset 容器内部的排序规则:

std::multiset<std::string, std::greater<string> > mymultiset{
    "http://c.biancheng.net/java/",
    "http://c.biancheng.net/stl/",
    "http://c.biancheng.net/python/" };

通过选用std::greater<string>降序规则,mymultiset 容器中元素的存储顺序为:

"http://c.biancheng.net/stl/"
"http://c.biancheng.net/python/"
"http://c.biancheng.net/java/"

Guess you like

Origin blog.csdn.net/crossoverpptx/article/details/131743711