Part3:关联容器(二)&Part4:迭代器

  • Rule4:当关乎效率时应该在map::operator[]和map-insert之间仔细选择

map[key] = value;这是一种简要的写法,这个操作是,如果key值不存在,插入这样一个键值对,如果key值存在,则修改这个键的值。

结论是:
当给map添加一个元素时,我们断定insert比operator[]好;而当更新已经在map里的元素值时operator[]更好。

  • Rule25:熟悉非标准散列容器
    STL标准C++库中没有任何散列表。但是兼容STL的散列关联容器可以从多个来源获得,名称如 hash_set,hash_multiset,hash_map和hash_multimap。
    事实上,在现在的编译器中,这些名称已经被标准化,已经成为了STL的一部分。

下面是迭代器的分界线
标准STL容器中提供了四种不同的迭代器:Iterator,const_iterator,reverse_iterator,const_reverse_iterator。

  • Rule26:尽量用Iterator代替其他三种迭代器
  • Rule27:用distance和advance把const_iterator转化为Iterator
    有些容器成员函数只接受Iterator作为参数,比如insert,erase函数。如果你有一个const_iterator,而你要在他所指向的容器位置上插入新元素?此时需要想办法把const_Iterator转化为Iterator。

我们知道可以使用const_cast进行去常量化。但是如下代码却有问题。

    typedef deque<int> IntDeque;
    typedef IntDeque::iterator Iter;
    typedef IntDeque::const_iterator ConstIter;
    ConstIter ci;

    Iter i = const_cast<Iter>(ci);

这里只是以deque为例,但是用其他容器类:list,set,multiset,map,multimap甚至是散列表容器,都是一样的结果(vector或string的代码能够通过编译,但是这是非常特殊的情形)。包含映射的代码不能通过编译的原因在于Iterator和const_Iterator是完全不同的类。在两个毫无关联的类之间进行const_cast映射是完全荒谬的。
不能编译的代码对于vector和string容器来说也许能够通过编译。那是因为通常情况下大多数实现都会采用真实的指针作为那些容器的迭代器。就这种实现而言,vector< T >::iterator是T*的typedef,而vector< T >::const_iterator是const T*的typedef,string::iterator是char*的typedef,而string::const_iterator是const char*的typedef。在这种实现的情况下,const_Iterator与Iterator之间的const_cast映射被最终解释成const T*到 T*的映射。但是即使这种实现中,reverse_Iterator和cosnt_reverse_iterator也是真正的类,不能将他俩进行映射。

如果你得到一个const_Iterator并且可以访问它所指向的容器,有一种安全的,可移植的方法获取它所对应的Iterator。

    typedef deque<int> IntDeque;
    typedef IntDeque::iterator Iter;
    typedef IntDeque::const_iterator ConstIter;
    ConstIter ci;

    //Iter i = const_cast<Iter>(ci);

    IntDeque  d;
    ConstIter ci;
    //...                   // 让ci指向d
    Iter i(d.begin());              // 初始化i为d.begin()
    advance(i, distance<ConstIter>(i, ci)); 

要得到与const_iterator指向同一位置的iterator,首先将iterator指向容器的起始位置,然后把它向前移到和const_iterator距离容器起始位置的偏移量一样的位置即可!这个任务得到了两个函数模板advance和distance的帮助,它们都在< iterator >中声明。distance返回两个指向同一个容器的iterator之间的距离;advance则用于将一个iterator移动指定的距离。如果i和ci指向同一个容器,那么表达式advance(i, distance(i, ci))会将i移动到与ci相同的位置上。

  • Rule28:了解如何通过reverse_iterator的base得到Iterator
    我们分析如下代码:
    这里写图片描述
    这就是 ri.base()与ri的位置关系。
    这样,我们通过reverse_iterator 进行插入或者删除元素时就有一些相对位置关系要考虑(因为插入删除操作只支持Iterator类型,不支持reverse_iterator):
    如果是插入操作:
    我们假设你要插入的值是99。记住ri在上图中遍历的顺序是自右向左,而且插入操作会将新元素插入到ri位置,并且将原先ri位置的元素移到遍历过程的“下一个”位置,我们认为3应该出现在99的左侧。当ri指向3时,i(就是ri.base())指向4。如果我们用ri来指定插入位置,那么用i指向插入位置,那个假设就是正确的。
    如果是删除操作:
    如果你要删除ri指向的元素,你不能直接使用i了,因为i与ri不是指向同一个元素。因此,你要删除的是i的前一个元素。
    要实现在一个reverse_iterator ri指出的位置上删除元素,就应该删除ri.base()的前一个元素。对于删除操作而言,ri和ri.base()并不等价,而且ri.base()不是ri对应的iterator。
    我们使用这个代码:
v.erase((++ri).base()); 

注意:

v.erase(--ri.base());   

这种写法的问题:
这个设计并不存在什么问题。表达式–ri.base()确实能够指出我们需要删除的元素。而且,它们能够处理除了vector和string之外的其他所有容器。它可能也能处理vector和string,但对于大多数vector和string的实现,它无法通过编译。在这样的实现下,iterator(和const_iterator)会采用内建的指针来实现,所以ri.base()的结果是一个指针。都规定了不能直接修改函数返回的指针,所以在string和vector的迭代器是指针的STL平台上,像–ri.base()这样的表达式无法通过编译。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值