条款18:避免使用vector<bool>(转)

来源:互联网 发布:淘宝高达模型的黑店 编辑:程序博客网 时间:2024/05/16 12:01

做为一个STL容器,vector< bool>确实只有两个问题。第一,它不是一个STL容器。第二,它并不容纳bool。除此以外,就没有什么要反对的了。

一个东西不能成为STL容器只因为会有人会说它是。一个东西要成为STL容器就必须满足所有在C++标准23.1节中列出的容器必要条件。在这些要求中有这样一条:如果c是一个T类型对象的容器,且c支持operator[],那么以下代码必须能够编译:

T *p = &c[0];   // 无论operator[]返回什么,    // 都可以用这个地址初始化一个T*

换句话说,如果你使用operator[]来得到Container中的一个T对象,你可以通过取它的地址而获得指向那个对象的指针。(假设T没有倔强地重载一些操作符。)然而如果vector< bool>是一个容器,这段代码必须能够编译:

vector<bool> v;bool *pb = &v[0];   // 用vector<bool>::operator[]返回的    // 东西的地址初始化一个bool*

但它不能编译。因为vector< bool>是一个伪容器,并不保存真正的bool,而是打包bool以节省空间。在一个典型的实现中,每个保存在“vector”中的“bool”占用一个单独的比特,而一个8比特的字节将容纳8个“bool”。在内部,vector使用了与位域(bitfield)等价的思想来表示它假装容纳的bool。

正如bool,位域也只表现为两种可能的值,但真的bool和化装成bool的位域之间有一个重要的不同:你可以创建指向真的bool的指针,但却禁止有指向单个比特的指针。

引用单个比特也是禁止的,这为vector接口的设计摆出了难题。因为vector< T >::operator[]的返回值应该是T&。如果vector< bool >真正容纳bool,这不成问题,但因为它没有,vector< bool >::operator[]需要返回指向一个比特的引用,而并不存在这样的东西。

为了解决这个难题,vector< bool >::operator[]返回一个对象,其行为类似于比特的引用,也称为代理对象。(如果仅使用STL,你并不需要明白什么是代理对象,但它是一项值得了解的C++技术。关于代理对象的信息,参考《More Effective C++》的条款30,还有Gamma等人的《设计模式》中的“Proxy”章节。)深入本质来看,vector< bool >看起来像这样:

template < typename Allocator>vector<bool, Allocator> {public: class reference {...};   // 用于产生引用独立比特的代理类 reference operator[](size_type n);  // operator[]返回一个代理 ...}

现在,这段代码不能编译的原因就很明显了:

vector<bool> v;bool *pb = &v[0];   // 错误!右边的表达式是// < bool >::reference*类型,// 不是bool*

因为它不能编译,所以vector< bool >不满足STL容器的必要条件。是的,vector< bool >是在标准中,是的,它几乎满足了所有STL容器的必要条件,但是几乎还不够好。你写的有关STL容器的模板越多,会越深刻地认识到这一点。那天会来的。我保证,当你会写出一个模板,它只在取容器元素的地址时会产生一个指向包含类型的指针时才能工作,到那时,你将突然明白容器和几乎是容器之间的区别。

也许你想知道为什么vector< bool >存在于标准中,而它并不是一个容器。答案是与一个失败的高贵实验有关,但让我们推迟一下那个讨论,我有一个更紧迫的问题。如果vector< bool >应避免,因为它不是一个容器,那当我需要一个vector< bool >时应该用什么?

标准库提供了两个替代品,它们能满足几乎所有需要。第一个是deque< bool >。deque提供了几乎所有vector所提供的(唯一值得注意的是reserve和capacity),而deque< bool >是一个STL容器,它保存真正的bool值。当然,deque内部内存不是连续的。所以不能传递deque< bool >中的数据给一个希望得到bool数组的C API[1](参见条款16),但你也不能让vector< bool >做这一点,因为没有可移植的取得vector< bool >中数据的方法。(条款16中用于vector的技术不能在vector< bool >上通过编译,因为它们依赖于能够取得指向容器中包含的元素类型的指针。我提到过vector< bool >中不保存bool值吧?)

第二个vector< bool >的替代品是bitset。bitset不是一个STL容器,但它是C++标准库的一部分。与STL容器不同,它的大小(元素数量)在编译期固定,因此它不支持插入和删除元素。此外,因为它不是一个STL容器,它也不支持iterator。但就像vector< bool >,它使用一个压缩的表示法,使得它包含的每个值只占用一比特。它提供vector< bool >特有的flip成员函数,还有一系列其他操作位集(collection of bits)所特有的成员函数。如果不在乎没有迭代器和动态改变大小,你也许会发现bitset正合你意。

现在我们来讨论那个失败的高贵实验,它遗留下的残渣就是STL非容器的vector< bool >。我前面提到代理对象在C++软件开发中十分有用。C++标准委员会的成员当然也明白,他们决定开发vector< bool >做为说明STL可以支持包含通过代理访问元素的容器的演示。它们的理由很从分,有了这个例子在标准中,开发者将有一个现成的参考来实现自己的基于代理的容器。

唉,他们发现的是不可能创建满足所有STL容器的需要的基于代理的容器。但因为某种原因,他们没有尝试在标准中再开发一个。你可以去推测为什么保留vector< bool >,但现实地说,这没关系。重要的是:vector< bool >不满足STL容器的必要条件,你最好不要使用它;而deque< bool >和bitset是基本能满足你对vector< bool >提供的性能的需要的替代数据结构。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 考研团员档案丢了怎么办 学生的档案丢了怎么办 大四团员证丢了怎么办 2岁宝宝吸收不好怎么办 孩子逆反心理特强沟通不了怎么办 一年级孩子有逆反心里怎么办 如果一个学生会部门没人留怎么办 如果学生会部门很少人报名怎么办 职场说错话了我该怎么办? 中考通知书收到两份怎么办 高三学生英语差怎么办 论文查重百分之0怎么办 微信红包发错人了怎么办 文学社在社团活动日可以怎么办 小孩玩游戏瞎花钱怎么办 党员不配合纪检调查工作怎么办 新同事老问问题怎么办 白色衣服染红色了怎么办 左腿膝盖内侧疼怎么办 差二本线几分怎么办 吃错东西了想吐怎么办 玩游戏扣的话费怎么办 转笔实在是不会怎么办 滑板l轴承沾水了怎么办 暗影格斗3闪退怎么办 暗影格斗3文档被删怎么办 dnf手残党偷学技能学不了怎么办 打篮球没热身膝盖酸痛是怎么办 无线路由器lan口少怎么办? 电脑ip设置乱了怎么办 监控拍我我偷钱怎么办 网吧上网密码忘记了怎么办 比熊犬晚上叫怎么办 刚买的狗一直叫怎么办 酒驾罚款2年没交怎么办 法院判罚款没有钱怎么办 有人朝你吐口水怎么办 孕妇用了六神花露水怎么办 出车祸人不赔钱怎么办 交通事故对方保险不签字怎么办 肇事车主联系不上怎么办