Effective STL 读书笔记 4

本文详细探讨了STL中的关联容器和顺序容器的区别,重点介绍了等价与相等的概念及其在容器中的应用,并讨论了如何优化set和map的操作,以及替代方案如sorted vector和非标准散列容器的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Item 19:区分等价(equivalence)和相等(equality)。
  • 现看看英汉字典对等价(equivalence)的定义:
一种逻辑操作符,具有下述性质:若P是一个命题,Q是一个命题,R是一个命题,当且仅当 所有的命题为或所有的命题为时,P,Q,R,...的等价才为
  • 在 STL 中有两种比较对象是否相等的方法,方法一,相等(equality):
x == y
  • 方法二,等价(equivalence):
!(x < y) && !(y < x)
  • 标准关联容器需要对元素进行排序,对于每个容器必须有一个比较算法(比较对象的大小,而不仅仅是是否相等),默认的算法是 less,用户必须定义 operator < 及 operator == 。为了减少冲突和减少用户的编码量,STL 用等价代替相等,这样用户即可将 operator == 省略。
  • 总结:关联容器使用等价,顺序容器使用相等。

Item 20:为成员是指针类型的关联容器提供比较类型。
  • stl::set<typename T, typename Compare> 其中 Compare 是一个 type 而不是一个函数指针、或者函数对象,这就是所谓的“比较类型(comparison types)”。

Item 21:当对象相等时,总是让比较函数返回 false。
  • 当比较函数是 < 或 > 时,此条目是废话。
  • 当比较函数是 <= 或 >= 时,如果 x == y 时使 <= 及 >= 返回 true,则会破坏等价性(见Item 19),从而破坏关联容器。
x == y 与 !(x <= y) && !(y <= x) 冲突。


Item 22:避免对 set 及 multiset 的关键部分进行 in-place 修改。
  • 首先看看 wikipedia 对 in-place 的定义:
In computer science, an in-place algorithm is an algorithm which transforms a data structure using a small, constant amount of extra storage space. The input is usually overwritten by the output as the algorithm executes.
  • 这里的“关键部分(key part)”指的是 set / multiset 存储的对象 T 中对 set / multiset 的排序算法有影响的部分,或者说是参与排序的部分。比如下例中 User::ID:
class User {
public:
    unsigned int ID;
    std::string name;
    unsinged int age;
};

class UserIDLess : public binary_function<User, User, bool> {
public:
    operator() (const User &lhs, const User &rhs) const {
       return lhs.ID < rhs.ID;
    }
};

typedef set<User, UserIDLess> IDSortedUserSet;
  • 修改 set / multiset 中的对象时,注意不要改变对象的关键部分,否则容器会被破坏。
  • 如果必须改变关键部分,采取如下策略:
1. 找到要修改的对象。*a = set.find();
2. 复制对象。*b = new Obj(*a);
3. 修改复制的对象。b->changeSome();
4. 删除原对象。set.remove(a);
5. 插入复制的对象。set.insert(b);


Item 23:考虑用 sorted vector 代替关联容器。
  • 关联容器对频繁的插入、删除、查找作了优化。但我们的操作模式通常是:首先建立容器(向容器中插入数据),然后使用容器(在容器中查找对象,多次),修改容器(如果有必要)。在这样的情况下我们可以使用 sorted vector 替代关联容器。
  • 理由一:关联容器的内部实现是平衡二叉树,比 vector 所占用的内存要大,特别是在对象较小而数量众多的情况下。
  • 理由二:当占用内存数超过一个物理内存页面(4K )大小时,关联容器的各种算法均会导致频繁的页交换,而 vector 则会好的多。

Item24:在效率很重要的情况下,仔细选择 map::operator[] 和 map::insert。
  • map::operator[] 采用的是 "add or update" 策略。
  • 当指定键值不存在时,map[key] = value 与下例等同:
pair<Map::iterator, bool> result =
    map.insert(Map::value_type(key, MapValue());
result.first->second = value;
  • 效率比 map::insert 差,替代方案:
template<typename Map, typename MapKey, typename MapValue>
typename Map::iterator
efficientAddOrUpdate(Map& map, const MapKey &key, const MapValue value) {
    typename Map::iterator itr = map.lower_bound(key);
    if (itr != map.end() && !(map.key_comp()(key, itr->first))) {
       itr->second = value;
       return itr;
    }
    else {
       return map.insert(itr, Map::value_type(key, value));
    }
}
  • 上面使用了 hinted insert,有较高效率。

Item 25:熟悉非标准的散列容器(hashed containers)。
  • 为了不推迟 STL 的发布,标准委员会没有将 hashed container 放入标准中。(下一版会加入?)
  • 但我们有事实上的 hash 容器:hash_set, hash_multiset, hash_map, hash_multimap.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值