Effective STL_读书笔记

Effective STL

1. 容器

条例01:慎重选择容器类型

如何在vector、list、deque中做出选择:

  1. vector是默认应使用的序列类型;
  2. 当需要频繁的在序列中将做插入和删除操作时,应使用list。
  3. 当大多数插入和删除操作发生在序列的头部和尾部时,deque是应该考虑的数据结构。

连续内存容器:把它的元素存放在一块或者多块内存中,每块内存中存多个元素。但有新元素插入或者已有的元素被删除时,同一内存块中的其他元素要向前或者向后移动,以便为新元素让出空间,或者填充被删除元素留下的空隙。这种移动影响到效率和异常安全性。

标准的连续内存容器包括:vector、string、deque。

基于节点的容器:在每一个内存块中只存放一个元素。容器中元素的插入或者删除只影响到指向节点的指针,而不影响节点本身的内容,所以当有插入或者删除操作时,元素的值不需要移动。

基于节点的容器:list、所有标准的关联容器。

对插入和删除操作,如果需要事务语义(也就是说,在插入和删除操作失败时,需要回滚的能力),你就要使用基于节点的容器。如果对多个元素的插入操作需要事务语义,则你需要选择list,因为在标准容器中,只有list对多个元素的插入操作提供了事务语义。对那些希望编写异常安全代码的程序员,事务语义显得尤为重要。

string是STL中在swap过程中会导致迭代器、指针和引用变为无效的唯一容器。

条例02:不要试图编写独立于容器类型的代码

STL是以泛化原则为基础的:数组被泛化为“以其包含的对象类型为参数”的容器;函数被泛化为“以其使用的迭代器的类型为参数”的算法;指针被泛化为“以其指向的对象的类型为参数”的迭代器。

很多程序员想以一种不同的方式做泛化。他们在自己的软件中不是针对某种具体的容器,而是想把容器的概念泛化,这样他们就能使用,比如vector,而仍保留以后将其换成deque或list的选择——但不必改变使用该容器的代码。这种想法是不正确的!!!

试图编写独立于容器类型的代码——是不正确的!

条例03:确保容器中对象的拷贝正确而高效

容器中保存了对象,但并不是你提供给容器的那些对象。当你通过insert或者push_back向容器中加入对象时,存入容器的是你所指定的对象的拷贝。

如果向容器中填充对象,而对象的拷贝又非常耗时,那么向容器中填充对象这一操作可能成为程序的性能瓶颈。

在继承体系下,拷贝动作可能导致剥离(slicing)。也就是说,如果你创建一个存放基类对象的容器,却向其中插入派生类对象,那么派生类对象被拷贝进容器时,它所有的特性部分将被丢失。

剥离问题意味着向基类对象容器中插入派生类对象几乎总是错的。

使拷贝动作高效、正确并防止剥离问题发生的一个简单办法就是使容器包含指针而非对象。

条例04:调用empty而不是检查size()是否为空

empty对所有标准容器都是常数时间操作,而对于一些list实现,size()耗费线性时间。

条例05:区间成员函数优先于与之对应的单元素成员函数

给定v1和v2两个向量(vector),使v1的内容和v2的后半部分相同的最简单操作是什么?

v1.assign(v2.begin()+v2.size()/2 , v2.end());

使用区间成员函数的优点:

  1. 通过使用区间成员函数,通常可以少写一些代码。
  2. 使用区间成员函数通常可以得到意图清晰或更加直接的代码

当处理标准序列容器时,为了取得同样的结果,使用单元素的成员函数比使用区间成员函数需要更多地调用内存分配子,更频繁地拷贝对象,而且/或者做冗余的操作。

条例06:当心C++编译器最烦人的分析机制

class Widget{...};
Widget w();

此处并没有声明一个名为w的Widget类型变量,而是声明了一个名为w的函数,该函数不带任何参数。

C++中的一条普遍规律相符,即尽可能地解释为函数声明。

条例07:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉

指针容器在自己被析构时会析构掉包含的每一个元素,当指针的析构函数什么都不做,当然也不会调用delete。

从没有析构函数的类进行共有继承是C++中的一项重要禁忌。

STL容器很智能,但没有智能到知道是否该删除自己所包含的指针的程度。当你使用指针的容器,而其中的指针应该被删除时,为了避免资源泄漏,你必须或者用引用计数形式的智能指针对象(比如Boost的shared_ptr)代替指针,或者当容器被析构时手工删除其中的每个指针。

条例08:切勿创建包含auto_ptr的容器对象

auto_ptr的容器(简称COAP)是被禁止的。

条例09:慎重选择删除元素的方法

对于删除特定元素:

对于连续内容容器(vector、deque、string)——使用erase-remove的方式进行删除元素

c.erase(remove(c.begin(),c.end(), 1963), c.end());

对于list,直接调用remove()接口进行元素删除。

对于标准关联容器,使用erase()接口删除元素。只需要对数时间开销。

关联容器的erase函数是基于等价而不是相等。

对于删除符合条件的元素:

对于连续内容容器(vector、deque、string)——使用erase-remove_if的方式进行删除元素

c.erase(remove_if(c.begin(),c.end(), Func), c.end());

对于list,直接调用remove_if()接口进行元素删除。

对于标准关联容器:有两种方式可以解决问题。

  1. 利用remove_copy_if把我们需要的值复制到一个新容器中,然后把原来容器的内容和新容器的内容相互交换。
  2. for循环中调用容器的erase()接口函数。
for(auto iter = c.begin(); iter!=c.end();)
{
    if(func(*iter))
    {
        c.erase(iter++);
    }
    else
    {
        iter++;
    }
}

对于标准序列容器,使用for进行元素删除时,一定要利用erase()函数的返回值。

for(auto iter = c.begin(); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值