如何修改关联式容器中键值对的键?
通过前面的学习,读者已经掌握了所有关联式容器(包括 map、multimap、set 和 multiset)的特性和用法。其中需要指出的是,对于如何修改容器中某个键值对的键,所有关联式容器可以采用同一种解决思路,即先删除该键值对,然后再向容器中添加修改之后的新键值对。
那么,是否可以不删除目标键值对,而直接修改它的键呢?接下来就围绕此问题,给读者展开详细的讲解。
首先可以明确的是,map 和 multimap 容器只能采用“先删除,再添加”的方式修改某个键值对的键。原因很简单,C++ STL 标准中明确规定,map 和 multimap 容器用于存储类型为 pair<const K, V> 的键值对。显然,只要目标键值对存储在当前容器中,键的值就无法被修改。
举个例子:
正如上面例子中演示的那样,直接修改 map 或 multimap 容器中某个键值对的键是行不通的。但对于 set 或者 multiset 容器来说,却是可行的。
和 map、multimap 不同,C++ STL 标准中并没有用 const 限定 set 和 multiset 容器中存储元素的类型。换句话说,对于 set<T> 或者 multiset<T> 类型的容器,其存储元素的类型是 T 而不是 const T。
事实上,对 set 和 multiset 容器中的元素类型作 const 修饰,是违背常理的。举个例子,假设我们使用 set 容器存储多个学生信息,如下是一个表示学生的类:
虽然 C++ STL 标准没有用 const 修饰 set 或者 multiset 容器中元素的类型,但也做了其它工作来限制用户修改容器的元素。例如上面代码中,*iter 会调用 operator*,其返回的是一个 const T& 类型元素。这意味着,C++ STL 标准不允许用户借助迭代器来直接修改 set 或者 multiset 容器中的元素。
那么,如何才能正确修改 set 或 multiset 容器中的元素呢?最直接的方式就是借助 const_cast 运算符,该运算符可以去掉指针或者引用的 const 限定符。
为了加深读者的理解,如下是和本节内容相关的完整程序,读者可直接拷贝下来:
那么,是否可以不删除目标键值对,而直接修改它的键呢?接下来就围绕此问题,给读者展开详细的讲解。
首先可以明确的是,map 和 multimap 容器只能采用“先删除,再添加”的方式修改某个键值对的键。原因很简单,C++ STL 标准中明确规定,map 和 multimap 容器用于存储类型为 pair<const K, V> 的键值对。显然,只要目标键值对存储在当前容器中,键的值就无法被修改。
举个例子:
map<int, int> mymap{ {1,10},{2,20} }; //map 容器的键为 const 类型,不能被修改 mymap.begin()->first = 100; multimap<int, int> mymultimap{ {10,100},{20,200} }; //multimap 容器的键为 const 类型,同样不能被修改 mymultimap.begin()->first = 100;其中,第 3 行代码试图直接将 mymap 容器中 {1,10} 的键改为 100,同样第 7 行代码试图直接将 mymultimap 容器中 {10,100} 的键改为 100,它们都是不能通过编译的。
正如上面例子中演示的那样,直接修改 map 或 multimap 容器中某个键值对的键是行不通的。但对于 set 或者 multiset 容器来说,却是可行的。
和 map、multimap 不同,C++ STL 标准中并没有用 const 限定 set 和 multiset 容器中存储元素的类型。换句话说,对于 set<T> 或者 multiset<T> 类型的容器,其存储元素的类型是 T 而不是 const T。
事实上,对 set 和 multiset 容器中的元素类型作 const 修饰,是违背常理的。举个例子,假设我们使用 set 容器存储多个学生信息,如下是一个表示学生的类:
class student { public: student(string name, int id, int age) :name(name), id(id), age(age) { } const int& getid() const { return id; } void setname(const string name){ this->name = name; } string getname() const{ return name; } void setage(int age){ this->age = age; } int getage() const{ return age; } private: string name; int id; int age; };在创建 set 容器之前,我们还需要为其设计一个排序规则,这里假定以每个学生的 id 做升序排序,其排序规则如下:
class cmp { public: bool operator ()(const student &stua, const student &stub) { //按照字符串的长度,做升序排序(即存储的字符串从短到长) return stua.getid() < stub.getid(); } };做完以上所有的准备工作后,就可以创建一个可存储 student 对象的 set 容器了,比如:
set<student, cmp> myset{ {"zhangsan",10,20},{"lisi",20,21},{"wangwu",15,19} };由此创建的 myset 容器中,存储的数据依次为:
{"zhangsan",10,20}
{"wangwu",15,19}
{"lisi",20,21}
例如,在已创建好的 myset 容器的基础上,如下代码尝试修改 myset 容器中某个学生的 name 名字:总之,set 和 multiset 容器的元素类型没有用 const 修饰。所以从语法的角度分析,我们可以直接修改容器中元素的值,但一定不要修改元素的键。
set<student>::iterator iter = mymap.begin(); (*iter).setname("xiaoming");注意,如果读者运行代码会发现,它也是无法通过编译的。
虽然 C++ STL 标准没有用 const 修饰 set 或者 multiset 容器中元素的类型,但也做了其它工作来限制用户修改容器的元素。例如上面代码中,*iter 会调用 operator*,其返回的是一个 const T& 类型元素。这意味着,C++ STL 标准不允许用户借助迭代器来直接修改 set 或者 multiset 容器中的元素。
那么,如何才能正确修改 set 或 multiset 容器中的元素呢?最直接的方式就是借助 const_cast 运算符,该运算符可以去掉指针或者引用的 const 限定符。
比如,我们只需要借助 const_cast 运算符对上面程序稍作修改,就可以运行成功:有关 const_cast 运算符的用法,由于不是本节重点,这里不再做详细讲解,有兴趣的读者可自行查阅相关资料。
set<student>::iterator iter = mymap.begin(); const_cast<student&>(*iter).setname("xiaoming");由此,mymap 容器中的 {"zhangsan",10,20} 就变成了 {"xiaoming",10,20}。
再次强调,虽然使用 const_cast 能直接修改 set 或者 multiset 容器中的元素,但一定不要修改元素的键!如果要修改,只能采用“先删除,再添加”的方式。另外,不要试图以同样的方式修改 map 或者 multimap 容器中键值对的键,这违反了 C++ STL 标准的规定。
总结
总的来说,map 和 multimap 容器中元素的键是无法直接修改的,但借助 const_cast,我们可以直接修改 set 和 multiset 容器中元素的非键部分。为了加深读者的理解,如下是和本节内容相关的完整程序,读者可直接拷贝下来:
#include<iostream> #include<set> #include<string> using namespace std; class student { public: student(string name, int id, int age) :name(name), id(id), age(age) { } const int& getid() const { return id; } void setname(const string name){ this->name = name; } string getname() const{ return name; } void setage(int age){ this->age = age; } int getage() const{ return age; } void display()const { cout << id << " " << name << " " << age << endl; } private: string name; int id; int age; }; //自定义 myset 容器的排序规则 class cmp { public: bool operator ()(const student &stua, const student &stub) { //按照字符串的长度,做升序排序(即存储的字符串从短到长) return stua.getid() < stub.getid(); } }; int main() { set<student, cmp> mymap{ {"zhangsan",10,20},{"lisi",20,21},{"wangwu",15,19} }; set<student>::iterator iter = mymap.begin(); //直接将 {"zhangsan",10,20} 中的 "zhangsan" 修改为 "xiaoming" const_cast<student&>(*iter).setname("xiaoming"); while (iter != mymap.end()) { (*iter).display(); ++iter; } return 0; }程序执行结果为:
10 xiaoming 20
15 wangwu 19
20 lisi 21
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算