stl容器使用中的经验(五)--不要随意修改set或者multiset的键

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等容器中的元素:

  1. 在容器中找到要修改的元素;
  2. 进行元素拷贝;
  3. 修改拷贝;
  4. 删除原元素;
  5. 将拷贝插入容器相同的位置。
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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值