effective stl 第22条:切勿直接修改set和multiset中的键
2016-09-22 21:12
232 查看
#include<iostream> #include<iterator> #include<set> #include<map> #include<algorithm> using namespace std; class Employee { public: Employee(); ~Employee(); const string& name() const;//获取雇员的名字 void setName(const string& name);//设置雇员的名字 int idNumber() const;//获取雇员的ID private: }; struct IDBumberLess: public binary_function<Employee, Employee, bool>{ bool operator()(const Employee& lhs, const Employee& hls) const { return lhs.idNumber < hls.idNumber; } }; Employee::Employee() { } Employee::~Employee() { } int main() { /* 对于map<k,v> 和 multimap<k,v> pair<const k,v>,因此键值是不能改变的,除非强制转换 而对于set<T>和multiset<T>,T不是const类型,因此是能改变的 */ typedef set<Employee, IDBumberLess> EmpSet; EmpSet se; Employee selectId;//存储有特定ID号的变量 EmpSet::iterator it = se.find(selectId); if (it!= se.end()) { it->setName("hahah");//错误,因为it返回的类型为const T&,所以不能修改 } //上边的列子表明,如果修改set或者是multiset中的元素,一定不能修改键值,元素的这部分信息会修改 //容器的排序性,如果修改了这部分的内容,可能会破坏该容器,但是这项限制只限于对象的键值,对于被包含 //的其他部分,则是完全开放的,如:上边的例子修改了employee的名字,这是允许的。(注意:为了说明的更清楚 //上边修改是不能实现的,可能是不同的STL实现的版本不同,VS上使用的STL是不行的,但是为了说明问题, //这里假设是可以修改的) //如果重视可移植性,则将const去掉,转换为 if (it != se.end()) { //它将取得it所指的对象,并告诉编译器把类型转换的结果当做一个指向(非const)的Employee的引用,然后改名字 const_cast<Employee&>(*it).setName("hahah"); } //也有人写出这样的代码: if (it != se.end()) { static_cast<Employee>(*it).setName("hahah"); } //或者是下边的代码 if (it != se.end()) { ((Employee)(*it)).setName("hahah"); } //上边两个代码都是作用于临时生成变量上,而不是*it上,因此原来的相关属性并没有改变 //上边的两种代码都能编译通过,因为他们是等价的,与下边的代码是相同的 if (it != se.end()) { Employee temp(*it); temp.setName("hahah"); } //现在,强制转换到引用的重要性就很清楚了,因为通过强制转换到引用类型,我们可以避免创建新的对象 //大多数的强制类型都可以避免 return 0; }
你肯定听过映射是危险的,而且我希望本书能清楚到让我相信你可以尽量避免它们的程度。进行映射将临时
剥去类型系统的安全性, 而且当你把安全网抛至脑后时,我们已经讨论的缺陷例证了所能发生的。
大多数映射可以避免,那包括我们刚刚考虑的。如果你要总是可以工作而且总是安全地改变set、multiset、
map或multimap里的元素,按五个简单的步骤去做:
1. 定位你想要改变的容器元素。如果你不确定最好的方法,条款45提供了关于怎样进行适当搜寻的指
导。
2. 拷贝一份要被修改的元素。对map或multimap而言,确定不要把副本的第一个元素声明为const。毕
竟,你想要改变它!
3. 修改副本,使它有你想要在容器里的值。
4. 从容器里删除元素,通常通过调用erase(参见条款9)。
5. 把新值插入容器。如果新元素在容器的排序顺序中的位置正好相同或相邻于删除的元素,使用insert
的“提示”形式把插入的效率从对数时间改进到分摊的常数时间。使用你从第一步获得的迭代器作为提示信息。
下边是安全的,可移植的方式编写的:
EmpSet::iterator it = se.find(selectId); if (it != se.end()) { Employee e(*it);//第1、2步 e.setName("hahah");//第3步 se.erase(it++);//第4步,可以参加第9条关于非顺序容器的erase se.insert(it, e);//第5步 }
对于set和multiset,如果直接对容器中的元素做了修改,那么要保证该容器仍然是排序的。
相关文章推荐
- effective STL - 避免原地修改set和multiset的键
- 避免原地修改set和multiset的键而修改其值
- STL中的set---可以直接修改set中的元素么?
- 条款22:避免原地修改set和multiset的键
- 你妹, 不修改word文档, 直接对word进行ctrl + s操作, md5值居然变了!
- 破解 jar 包之直接修改 .class 文件方式
- 子窗口直接修改调用它的父窗口的激发dom的值
- STL 之set和multiset(江南烟雨)
- 为什么直接复制项目修改名字后输入地址报404
- 直接修改jpbm xml流程定义字段的方法
- delphi验证码图像生成,修改自“名士:517165547”,避免出现容易混淆的0和O字符,直接生产TBitmap。
- HttpModule 实现 ASP.Net (*.aspx) 中文简繁体的自动转换,不用修改原有的任何代码,直接部署即可!
- 用PPT直接修改主集成模板,并保存为pps格式,即可现场展示应用.
- 在串口下添加set和get命令(修改Linux内核代码)
- ASP.NET中直接用C# 动态修改CSS样式 【wonsoft 】
- FastAdmin 添加新字段后,不显示,可以直接去修改对应的js
- Vue.set()动态的新增与修改数据,触发视图更新的方法
- mac上的pdf编辑器怎么才能直接修改PDF文档上的字体大小
- linux下通过sed命令直接修改文件内容
- 不加入域直接修改域用户密码