1、不要随意修改set或者multiset的键
对map和multimap来说,直接修改键会在编译器这一层报错。
map<_key, _value> m;
m.bengin()->first = 10; //编译失败
主要是因为对于map和multimap来说,他们的元素类型是
pair<const _Key, _Tp>
也就是说,_Key是const类型的,不允许修改。而对set或multiset来说,他们的元素的类型是 _Key,而不是const _Key的。为什么不是const类型呢?
因为,如果set中储存的是类的对象,每个元素可能会有一部分是可修改的,并不是都不可修改的。所以,我们会需要修改set中元素中并不会影响有关set有序性的部分。
class Worker
{
string& name() const;
void setName(const string& name);
int id() const; //不允许修改
string& title() const;
void setTitle(const string& title);
}
以上面的例子为例,或许我们应该保证其id不能被修改,因为工号是固定不便的,并不是说离职就会有另一个人顶替你的工号。所以工号是不能修改的。
但是对于name和title完全是可以修改的。每个工人都可能会晋升为主管,leader等等。所以这些非必要信息是可支持修改。
比如下面的例子中,有些stl认为其中修改title的语句是不合法的,因为*iter是const类型的。
typedef set<Worker> WorkerSet;
typedef WorkerSet::iterator WSIterator;
WorkerSet ws;
...
WSIterator iter = ws.find(selectId);
if(iter != ws.end())
{
*iter.setTitle("..."); //不一定能编译通过
}
所以,这样可移植性就会变得很差。
- 如果不考虑可移植性,并且使用的stl允许修改set或者multiset的元素的值,可进行修改,但要注意不要修改元素中有关键的部分,也就是有关set有序性的部分。
- 如果需要考虑可移植性,就要确保set或multiset中的元素不能被修改。至少不能未经过强制类型转换(cast)就修改。
我们需要做的是对迭代器做去除const 属性的强制类型转换。
if(iter != ws.end())
{
const_cast<Worker&>(*iter).setTitle("..."); //不一定能编译通过
}
谨防:
static_cast<Worker>(*iter).setTitle("...");
((Worker)(*iter)).setTitle("...");
上面的两种方法是等价的。可以正常编译,但并不能达到我们想要的结果。因为上面的代码在运行的过程中会生成一个临时的匿名对象,该对象是(*iter)的拷贝。并且在该对象的拷贝上调用setTitle方法。
但是强制类型转换是存在风险的,能避免就尽量不适用,我们总会找到一种方法来代替。安全的、可移植的修改set等容器中的元素:
- 在容器中找到要修改的元素;
- 进行元素拷贝;
- 修改拷贝;
- 删除原元素;
- 将拷贝插入容器相同的位置。
typedef set<Worker> WorkerSet;
typedef WorkerSet::iterator WSIterator;
WorkerSet ws;
...
WSIterator iter = ws.find(selectId);
if(iter != ws.end())
{
Worker w(*iter);
w.setTitle("...");
ws.earse(iter++); //删除后迭代器++,防止迭代器失效
ws.insert(iter, w);
}