您的位置:首页 > Web前端

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,如果直接对容器中的元素做了修改,那么要保证该容器仍然是排序的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: