网站可以有二维码吗,三北防护林体系建设网站,qq飞车哪个公司开发的,企业解决方案搞笑Effetive STL | 条款2 #xff1a; 小心对“容器无关代码”的幻想 文章目录 Effetive STL | 条款2 #xff1a; 小心对“容器无关代码”的幻想STL 容器特点推行自己的容器容器能力的交集 封装Method1: typedefMethod2: class 欢迎关注公众号【三戒纪元】…Effetive STL | 条款2 小心对“容器无关代码”的幻想 文章目录 Effetive STL | 条款2 小心对“容器无关代码”的幻想STL 容器特点推行自己的容器容器能力的交集 封装Method1: typedefMethod2: class 欢迎关注公众号【三戒纪元】 STL 容器特点
STL是建立在泛化之上的
数组泛化为容器参数化了所包含的对象的类型函数泛化为算法参数化了所用的迭代器的类型指针泛化为迭代器参数化了所指向的对象的类型
独立的容器类型泛化为序列或关联容器而且类似的容器拥有类似的功能。
标准的内存相邻容器都提供随机访问迭代器标准的基于节点的容器都提供双向迭代器。
序列容器支持push_front或push_back但关联容器不支持。关联容器提供对数时间复杂度的lower_bound、upper_bound和equal_range成员函数但序列容器却没有。
推行自己的容器
当你写你自己的容器、迭代器和算法时你会自然而然地推行它。
很多人会试图在他们的软件中泛化容器的不同而不是针对容器的特殊性编程他们会想在vector 中使用 deque 或者 list的特性这往往会带来麻烦。
比如 只有序列容器支持push_front或push_back只有关联容器支持count和lower_bound 即便是 insert和erase这样的操作在名称和语义上也有差别 当把对象插入序列容器中该对象会保留在你放置的位置上;当你把对象插入到一个关联容器中容器会按照排列顺序把对象移到它应该在的位置; 在序列容器上用一个迭代器作为参数调用 erase会返回一个新的迭代器在关联容器上什么都不返回。
容器能力的交集
如果你想写一个可以用在常用序列容器上的代码—— 包含vector, deque和list。你必须使用它们能力的交集来编写。
但要考虑几点
deque和list不支持reserve或capacitylist 不支持operator[] 操作且受限于双向迭代器的性能不能使用需要随机访问迭代器的算法包括sortstable_sortpartial_sort和nth_element如果想支持vector的规则则不能使用push_front和pop_frontvector和deque都会使splice和成员函数方式的sort失败因为deque::insert会使所有迭代器失效而且因为缺少capacityvector::insert也必须假设使所有指针和引用失效而deque是唯一一个在迭代器失效的情况下指针和引用仍然有效的东西不能把容器里的数据传递给C风格的界面只有vector支持这么做不能用bool作为保存的对象来实例化你的容器因为vector 并非总表现为一个vector实际上它并没有真正保存bool值。不能期望享受到list的常数时间复杂度的插入和删除vector和deque的插入和删除操作是线性时间复杂度的
所以真正开发时如果都考虑到上面几点那想开发的容器只剩下一个“泛化的序列容器”但是你不能调用reserve、capacity、operator[]、push_front、pop_front、splice或任何需要随机访问迭代器的算法调用insert和erase会有线性时间复杂度而且会使所有迭代器、指针和引用失效而且不能兼容C风格的界面不能存储bool。
如果你放弃了序列容器把代码改为只能和不同的关联容器配合这情况并没有什么改善。
要同时兼容set和map几乎是不可能的因为set保存单个对象而map保存对象对。甚至要同时兼容set和multiset或map和multimap也是很难的。set/map的insert成员函数只返回一个值和他们的multi兄弟的返回类型不同而且你必须避免对一个保存在容器中的值的拷贝份数作出任何假设。对于map和multimap你必须避免使用operator[]因为这个成员函数只存在于map中。
面对疾风吧
这根本没有必要。不同的容器是不同的而且它们的优点和缺点有重大不同。它们并不被设计成可互换的而且你做不了什么包装的工作。如果你想试试看你只不过是在考验命运但命运并不想被考验。
封装
如果想改变容器类型就使用封装。
Method1: typedef
一种最简单的方法是通过自由地对容器和迭代器类型使用typedef
class Widget {...};
vectorWidget vw;
Widget bestWidget;
... // 给bestWidget一个值
vectorWidget::iterator i // 寻找和bestWidget相等的Widget
find(vw.begin(), vw.end(), bestWidget);可以简化上述写法
class Widget { ... };
typedef vectorWidget WidgetContainer;
typedef WidgetContainer::iterator WCIterator;
WidgetContainer cw;
Widget bestWidget;
...
WCIterator i find(cw.begin(), cw.end(), bestWidg如果需要加上用户的allocator也特别方便。一个不影响对迭代器/指针/参考的失效规则的改变
class Widget { ... };
templatetypename T // 关于为什么这里需要一个template
SpecialAllocator { ... }; // 请参见条款10
typedef vectorWidget, SpecialAllocatorWidget WidgetContainer;
typedef WidgetContainer::iterator WCIterator;
WidgetContainer cw; // 仍然能用
Widget bestWidget;
...
WCIterator i find(cw.begin(), cw.end(), bestWidget); // 仍然能用typedef只是其它类型的同义字所以它提供的的封装是纯的词法译注不像#define是在预编译阶段替换的。typedef并不能阻止用户使用或依赖任何他们不应该用的或依赖的。
Method2: class
要限制如果用一个容器类型替换了另一个容器可能需要修改的代码就需要在类中隐藏那个容器而且要通过类的接口限制容器特殊信息可见性的数量。
比如需要隐藏 真实的容器 list 建立客户列表
class CustomerList {
private:
typedef listCustomer CustomerContainer;
typedef CustomerContainer::iterator CCIterator;
CustomerContainer customers;
public: // 通过这个接口
... // 限制list特殊信息的可见性
};如果使用过程中你发现从列表的中部插入和删除客户并不像你想象的那么频繁仅仅需要快速确定客户列表顶部的20%——一个为nth_element算法量身定做的任务。
但nth_element需要随机访问迭代器不能兼容list。
在这种情况下你的客户“list”可能更应该用vector或deque来实现
当你决定作这种更改的时候你仍然必须检查每个CustomerList的成员函数和每个友元看看他们受影响的程度根据性能和迭代器/指针/引用失效的情况等等。
但如果你做好了对CustomerList地实现细节做好封装的话那对CustomerList的客户的影响将会很小。 欢迎关注公众号【三戒纪元】