Effective STL 笔记

 

1.容器无关代码不存在,也就是不存在一种万能的容器,任何容器都有它存在的价值和用武之地.

 

2.因为容器类型可能会改变所以不要这么写:

class Widget {...};

vector<Widget> vw;

// ...

Widget bestWidget;

vector<Widget>::iterator i = find(vw.begin(), vw.end(), bestWidget);

 

而是这么写:

class Widget { ... };

typedef vector<Widget> WidgetContainer;

typedef WidgetContainer::iterator WCIterator;

WidgetContainer cw;

// ...

 

Widget bestWidget;

WCIterator i = find(cw.begin(), cw.end(), bestWidget);

 

3.如果你需要建立一个客户列表,请不要直接用list。取而代之的是,建立一个CustomerList类,把list隐藏在它的private区域:

class CustomerList

{

private:

     typedef std::list<Customer> CustomerContainer;

 

     typedef CustomerContainer::iterator CCIterator;

 

     CustomerContainer customers;

 

public:         // 通过这个接口

 

     ...         // 限制list特殊信息的可见性

};

 

 

4.用empty来代替检查size()是否为0

if (c.size() == 0)... 写为if (c.empty())...

于所有的标准容器,empty是一个常数时间的操作,但对于一些list实现,size花费线性时间, sizesplice的区间形式——必须让步。即如果要保持一个size的常数,就要在splice的时候遍历一般获得新的size,或者保持splice是常数,就要保证获得listsize要遍历一遍数据,也就是一个或者另一个可以是常数时间操作,但不能都是。


5. 尽量使用区间成员函数代替它们的单元素兄弟,区间成员函数是一个像STL算法的成员函数,使用两个迭代器参数来指定元素的一个区间来进行某个操作,它通常比单元素操作要直观和高效。

区间构造:

所有标准容器都提供这种形式的构造函数:

container::container(InputIterator begin,      // 区间的起点

                        InputIterator end);   // 区间的终点

 

区间插入:

所有标准序列容器都提供这种形式的insert

void container::insert(iterator position,      // 区间插入的位置

                          InputIterator begin, // 插入区间的起点

                          InputIterator end); // 插入区间的终点

 

关联容器使用它们的比较函数来决定元素要放在哪里,所以它们了省略position参数:

void container::insert(lnputIterator begin, InputIterator end);

 

区间删除。

每个标准容器都提供了一个区间形式的erase,但是序列和关联容器的返回类型不同。

序列容器提供了这个:

iterator container::erase(iterator begin, iterator end);

 

关联容器提供这个:

void container::erase(iterator begin, iterator end);

 

区间赋值:

所有标准序列容器都提供了区间形式的assign

void container::assign(InputIterator begin, InputIterator end);



 6.删除元素

 

全局的remove算法:

template <class ForwardIterator, class T>

ForwardIterator remove(ForwardIterator first, ForwardIterator last,

                          const T& value);

 

这个算法会把[first,last)中不等于value的元素放到这段区域的最前面,但是不会改变容器的size,也就是后面会余下一定数目(value相等元素的个数)的无效区域,并返回这个无效区域的first。为啥要这样,而不是把这些元素直接从容器中删除呢?

1.全局算法(非容器成员函数)不知道容器一些属性,比如容器的end,这个函数中的firstlast不一定是容器的firstend,因此它也没权利去移动容器的后续元素去弥补这个无效区域。

 

 

因此对于vectordequestring删除元素的最好方法是erase-remove惯用法:

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

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

 

 

对于list也可以这么做,但是list的成员函数remove更高效:

c.remove(nValue);

c.remove_if(badValue);

 

 

对于关联容器(setmultisetmapmultimap)应该是调用erase

c.erase(nValue);

对于需要条件badValue的情况,需要遍历关联容器,然后一个一个删除。

 

 

遍历删除需要注意的:

A.如果容器是标准序列容器,写一个循环来遍历容器元素,每当调用erase时记得都用它的返回值更新你的迭代器。因为对于序列容器erase不仅使所有指向被删元素的迭代器失效,也使被删元素之后的所有迭代器失效。

         SeqContainer<int> c;

// ...

for (SeqContainer<int>::iterator i = c.begin(); i != c.end();)

{

          if (badValue(*i))

          {

               logFile << "Erasing " << *i << '\n';

// 标准序列容器erase返回被删元素之后的元素的有效迭代器。

               i = c.erase(i);  

          }

          else

          {

               ++i;

          }

}

 

 

B.如果容器是标准关联容器,写一个循环来遍历容器元素,当你把迭代器传给erase时记得后置递增它。

AssocContainer<int> c;

// ...

for (AssocContainer<int>::iterator i = c.begin(); i != c.end();)

{

          if (badValue(*i))

          {

// 标准关联容器的erase的返回类型是void

               c.erase(i++);

          }

          else

          {

               ++i;

          }

}

 

7. 使用交换技巧来修整过剩容量

vector<Contestant>(contestants).swap(contestants);

 

contestant.swap的作用和swap(constestantsA, constestantsB)是一样的。用于交换2个容器的内容。

template <class Assignable>

void swap(Assignable& a, Assignable& b);

 

8.避免使用vector<bool>

很多STL的实现对于vector<bool>并不保存真正的bool,而是打包bool以节省空间。在一个典型的实现中,每个保存在“vector”中的“bool”占用一个单独的比特,而一个8比特的字节将容纳8“bool”这将使很多操作比较危险。

 

2个办法可以解决这个问题:

1.           deque<bool>deque提供了几乎所有vector所提供的(唯一值得注意的是reservecapacity),而deque<bool>是一个STL容器,它保存真正的bool值。

2.    使用bitset


你可能感兴趣的:(effective)