码迷,mamicode.com
首页 > 编程语言 > 详细

《C++标准程序库》笔记之二

时间:2015-12-04 00:45:00      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:

《C++标准程序库》笔记之二

本篇博客笔记顺序大体按照《C++标准程序库(第1版)》各章节顺序编排。

--------------------------------------------------------------------------------------------

6. STL 容器

6.1-1

本节讲述STL容器的共通能力。其中大部分都是必要条件,所有STL容器都必须满足那些条件。三个最最核心的能力是:

(1)所有容器提供的都是“value语意”而非“reference语意”。容器进行元素的安插操作时,内部实施的是拷贝操作,置于容器内。因此STL容器的每一个元素都必须能够被拷贝。如果你打算存放的对象不具有public copy构造函数,或者你要的不是副本(例如你要的是被多个容器共同容纳的元素),那么容器元素就只能是指针(指向对象)。参见上一篇博客C++标准程序库笔记之一,5.10节。

(2)总体而言,所有元素形成一个次序(oder)。也就是说,你可以依相同次序一次或多次遍历每个元素。每个容器都提供“可返回迭代器”的函数,运用那些迭代器你就可以遍历元素。这是STL算法赖以生存的关键接口。

(3)一般而言,各项操作并非绝对安全。调用者必须确保传给操作函数的参数符合需求。违反这些需求(例如使用非法索引)会导致未定义的行为。通常STL自己不会抛出异常。如果STL容器所调用的使用者自定操作抛出异常,会导致各不相同的行为。

容器的共通操作如表6.1

6.2 Vector

6.2-1 vector的容量之所以重要,有以下两个原因:

(1)一旦内存重新配置,和vector元素相关的所有references、pointers、iterators都会失效;

(2)内存重新配置很耗时间(安插就可能导致重新配置)。

vectors的容量不会缩减,我们便可确定,即使删除元素,其references、pointers、iterators也会继续有效,继续指向动作发生前的位置。然而安插操作却可能是references、pointers、iterators失效(内存重新配置)。

6.3 Deques

6.3-1 deque不支持对容量和内存重分配时机的控制。不过,deque的内存重分配优于vector,因为其内部结构显示,deque不必在内存重分配时复制所有元素。参见《STL源码剖析》

6.5 Sets 和 Multisets(红黑树)

有两种方式可以定义排序准则:

(1)例如: std::set<int, std::greater<int> > coll;

这种情况下,排序准则就是型别的一部分。因此型别系统确保“只有排序准则相同的容器才能被合并”。这是排序准则的通常指定法。更精确地说,第二参数是排序准则的型别,实际的排序准则是容器所产生的函数对象(function object,或称functor)。为了产生它,容器构造函数会调用“排序准则型别”的default构造函数。

1. 元素比较动作只能用于型别相同的容器。换言之,元素和排序准则必须有相同的型别,否则编译时期会产生型别方面的错误。如下:

std::set<float> c1;     // sorting criterion -> std::less<>
std::set<float, std::greater<float> > c2;     // sorting criterion -> std::less<>
...
if (c1 == c2)          // ERROR:different types
{
    ...
}

2. 我们也可以使用自定之排序准则,这里同时也是把仿函数当做排序准则

#include <iostream>
#include <string>
#include <set>
#include <algorithm>
using namespace std;

class Person
{
    public:
        string firstname() const;
        string lastname() const;
        ....
};
/* class for function predicate
* - operator() returns whether a person is less than another person
*/
class PersonSortCriterion
{
    public:
        bool operator() (const Person& p1, const Person& P2) const
        {    
            /* a person is less than another person
            * - if the last name is less
            * - if the last name is equal and the first name is less
            */
            return p1.lastname() < p1.lastname() ||
                (!(p2.lastname() < p1.lastname()) &&
                p1.firstname() < p2.firstname());
        }
};

int main()
{
    // declare set type with special sorting criterion
    typedef set<Person, PersonSortCriterion> PersonSet;

    // create such a collection
    PersonSet coll;
    ...

    // do something with the elements
    PersonSet::iterator pos;
    for(pos = coll.begin(); pos != coll.end(); ++pos)
    {
        ...
    }
    ...
}

(2)以构造函数参数定义之
这种情况下,同一个型别可以运用不同的排序准则(如下面程序例子),而排序准则的初始值或状态也可以不同。如果执行期才获得排序准则,而且需要用到不同的排序准则(但数据型别必须相同),此一方式可派上用场。

// 执行期指定排序准则,排序准则不同,但型别相同:
#include <iostream>
#include <set>
using namespace std;

// type for sorting criterion
template <class T>
class RuntimeCmp
{
    public:
        enum cmp_mode {normal, reverse};
    private:
        cmp_mode mode;
    public:
        // constructor for sorting criterion
        // - default criterion uses value normal
        RuntimeCmp(cmp_mode m = normal) : mode(m) { }

        // comparision of elements
        bool operator() (const T& t1, const T& t2) const
        {
            return mode == normal ? t1 < t2 : t2 < t1;
        }

        // comparision of sorting criteria
        bool operator== (const RuntimeCmp& rc)
        {
            return mode == rc.mode;
        }
};

// type of a set that uses this sorting criterion
typedef set<int, RuntimeCmp<int> > IntSet;

// forward declaration
void fill(IntSet& set);

int main()
{
    // create, fill, and print set with normal element order
    // - uses default sorting criterion
    IntSet coll1;
    fill(coll1);
    print_elements(coll1, "coll1 :");

    // create sorting criterion with reverse element order
    RuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);

    // create, fill, and print set with reverse element order
    IntSet coll2(reverse_order);
    fill(coll2);
    print_elements(coll2, "coll2 :");
    
    // assign elements AND sorting criterion
    coll1 = coll2;
    coll1.insert(3);
    print_elements(coll1, "coll1 :");
    
    // just to make sure...
    if(coll1.value_comp() == coll2.value_comp())
    {
        cout << "coll1 and coll2 have same sorting criterion" << endl;
    }
    else
    {
        cout << "coll1 and coll2 have different sorting criterion" << endl;
    }
}

void fill(IntSet& set)
{
    // fill insert elements in random order
    set.insert(4);
    set.insert(7);
    set.insert(5);
    set.insert(1);
    set.insert(6);
    set.insert(2);
    set.insert(5);
}

输出:
coll1:1 2 4 5 6 7
coll2:7 6 5 4 2 1
coll1:7 6 5 4 3 2 1 
coll1 and coll2 have same sorting criterion

1. 在这个程序中,RuntimeCmp<> 是一个简单的template,提供“执行期间面对任意型别定义一个排序准则”的泛化能力。其default构造函数采用默认值normal,按升序排序;你也可以将RuntimeCmp<>::reverse传递给构造函数,便能按降序排序。

2. 注意,coll1 和 coll2 拥有相同的型别(包括元素的型别int,和排序准则的型别RuntimeCmp),该型别即fill() 函数的参数型别。再请注意,assignment操作符不仅赋值了元素,也赋值了排序准则(否则任何一个赋值操作岂不会轻易危及排序准则)。

3. 请注意,排序准则也被用于元素相等性检验工作,当采用缺省排序准则时,两元素的相等性检验语句如下:

if (!(elem1 < elem2 || elem2 < elem1))

这样做的好处有三:

a. 只需传递一个参数作为排序准则;

b. 不必针对元素型别提供operator==;

c. 你可以对“相等性”有截然相反的定义,不过当心造成混淆。

6.6 Maps  和 Multimaps

Maps和Multimaps 的元素型别key 和 T, 必须满足一下两个条件:

(1)key/value 必须具备assignable(可赋值的)和copyable(可复制的)性质;

(2)对排序准则而言,key必须是comparable(可比较的)。

Maps,Multimaps的排序准则定义和Sets,Multisets类似。同样,元素的比较动作也只能用于型别相同的容器。换言之,容器的key,value,排序准则都必须有相同的型别,否则编译期会产生型别方面的错误。

再次强调,更易型算法不得用于关联式容器,针对关联式容器,提供的迭代器类型为const iterator。

6.7 其他STL容器

6.7-1

STL 是个框架,除了提供标准容器,它也允许你使用其他数据结构作为容器。你可以使用字符串或数组作为STL容器,也可以自行撰写特殊容器以满足特性需求。如果你自行撰写容器,仍可从诸如排序、合并等算法中受益。这样的框架正是“开放性封闭(Open-Closed)”原则的极佳范例:允许扩展,谢绝修改。

下面是是你的容器“STL化”的三种不同方法:

(1)The invasive approach(侵入性作法) 直接提供STL容器所需接口。特别是诸如begin() 和 end() 之类的常用函数。这种作法需以某种特定方式编写容器,所以是侵入性的。

以string为例:

Strings可被视为以字符为元素的一种容器;字符构成序列,你可以在序列上来回移动遍历。因此,标准的string类别提供了STL容器接口。Strings也提供成员函数begin()和end(),返回随机存取迭代器,可用来遍历整个string。

(2)The noninvasive approach(非侵入性作法)

由你撰写或提供特殊迭代器,作为算法和特殊容器间的界面。此一作法是非侵入性的,它所需要的只是“遍历容器所有元素”的能力——这是任何容器都能以某种形式展现的能力。

以Array为例:

Arrays可被视为一种STL容器,但array并不是类别,所以不提供begin() 和 end() 等成员函数,也不允许存在任何成员函数。在这里,我们只能采用非侵入性作法或包装法。采取非侵入性作法很简单,你只需要一个对象,它能够透过STL迭代器接口,遍历数值的所有元素。事实上这样的对象早已存在,它就是普通指针。STL设计之初就决定让迭代器拥有和普通指针相同的接口,于是你可以将普通指针当成迭代器来使用。

(3)The wrapper approach(包装法) 将上述两种方法加以组合,我们可以写一个外套类别来包装任何数据结构,并显示出与STL容器相似的接口。

6.7-2 Hash Tables

Hash Tables数据结构可用于群集身上,非常重要,却因为提议太晚,未能包含于C++标准程序库——“我们必须在某个时间点中止引入新功能,开始关注细节,否则工作永无止境”。 Hash tables相关内容可以参见《STL源码剖析》。

6.9-1

表6.33提供了一份STL容器能力一览表。

《C++标准程序库》笔记之二

标签:

原文地址:http://www.cnblogs.com/yyxt/p/5017942.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!