STL是一种泛型编程(generic programming)。面向对象编程关注的是编成的数据方面,而泛型编程关注的是算法。它们之间的共同点是抽象和创建可重用代码,单他们的理念决然不同。
泛型编程旨在编写独立于数据类型的代码。

16.4.1 为何使用迭代器
理解迭代器是理解STL的关键所在。模板使得算法独立于存储的数据类型,而迭代其使算法独立于使用的容器类型。因此,它们都是STL通用方法的重要组成部分。
为了解为何需要迭代器,我们来看如何为两种不同数据表现实现find函数,然后来看如何推广这种方法。首先来看一个在double数组中搜索特定值的函数,可以这样编写该函数:
double * find_ar(double * ar, int n, const double & val)
{
    for (int i = 0; i < n; i ++)
        if (ar[i] == val)
            return &ar[i];
    return 0;   // or, in C++11, return nullptr;
}
如果函数在数组中找到这样的值,则返回该值在数组中的地址,否则返回一个空指针。该函数使用下标来便利数组。可以用模板将这种算法推广到包含==运算符的、任意类型的数组。尽管这样,这种算法仍然与一种特定的数据结构(数组)关联在一起。
下面来看搜索另一种数据结构——链表的情况(第12章使用链表实现了Queue类)。链表由连接在一起的Node结构组成:
struct Node
{
    double item;
    Node * p_next;
};
假设有一个指向链表第一个节点的指针,每个节点的p_next指针都指向下一个节点,链表最后一个节点的p_next指针被设置为0,则可以这样编写find_ll()函数:
Node* find_ll(Node * head, const double & val)
{
    Node * start;
    for (start = head; start != 0; start = start->p_next)
        if (start->item == val)
            return start;
    return 0;
}
同样,也可以使用模板将这种算法推官到支持==运算符的任何数据类型的链表。然而,这种算法也是与特定的数据结构——链表关联在一起。
从实现细节上看,这两个find函数的算法是不同的:一个使用数组索引来便利元素,另一个则将start重置为start->p_next。但从冠以上说,这两种算法是相同的:将值依次与容器中的每一个值进行比较,知道找到匹配的位置。
泛型编程旨在使用同一个find函数来处理数组、链表或任何其他容器类型。即函数不仅独立于容器中存储的数据类型,而且独立于容器本身的数据结构。模板提供了存储在容器中的数据类型的通用表示,因此还需要便利容器中的值的通用表示,迭代器正式这样的通用表示。
要实现find函数,迭代器应具备的特征:
* 应能够对迭代其执行解除引用的操作,一边能够访问它引用的值。即如果p是一个迭代其,则应对*p进行定义。
* 应能够将一个迭代其赋给另一个。即如果p和q都是迭代器,则应对表达式p=q进行定义。
* 应能够将一个迭代器与另一个进行比较,看它们是否相等。即如果p和q都是迭代其,则应对p==q和p!=q进行定义。
* 应能够使用迭代其便利容器中的所有元素,这可以通过为迭代其p定义++p和p++来实现。
常规指针就能满足迭代器的要求,因此,可以这样重新编写find_arr()函数:
typedef double * iterator;
iterator find_ar(iterator ar, int n, const double double & val)
{
    for (int i = 0; i < n; i++, ar++)
        if (*ar == val)
            return ar;
    return 0;
}
然后可以修改函数参数,使之接受两个指示区间的指针参数,其中的一个指向数组的起始位置,另一个指向数组的超尾;同时函数可以通过返回尾指针,来指出没有找到要找的值。下面的find_ar()版本完成了这些修改:
typedef double * iterator;
iterator find_ar(iterator begin, iterator end, const double & val)
{
    iterator ar;
    for (ar = begin; ar != end; ar++)
        if (*ar == vale)
            return  ar;
    return end;     // indicates val not found
}
对于find_ll()函数,可以定义一个迭代器类,其中定义了运算符*和++:
struct Node
{
    double item;
    Node * p_next;
};

class iterator
{
    Node * pt;
public:
    iteraotr() :p t(0) {}
    iterator (Node * pn) : pt(n) {}
    double operator*() { return pt->item; }
    iterator& operator++()  // for ++it
    {
        pt = pt->next;
        return *this;
    }
    iterator oprator++(int) // for it++
    {
        iterator tmp = *this;
        pt = pt->next;
        return tmp;
    }
// ... oprator==(), operator!=(), etc.
};
为区分++运算符的前缀版本和后缀版本,C++将operator++作为前缀版本,将operator++(int)作为后缀版本:其中的参数永远也不会被用到,所以不比指定其名称。
这里重点不是如何定义iterator类,而是有了这样的类后,第二个find函数就可以这样编写了:
iterator find_ll(iterator head, const double & val)
{
    iterator start;
    for (start = head; start != 0; ++ start)
        if (*start == val)
            return start;
    return 0;
}
STL遵循上面介绍的方法。首先,每个容器类(vector、list、deque等)定义了相应的迭代其类型。对于其中的某个类,迭代其可能是指针;而对于另一个类,则肯呢哥是对象。不管实现方式如何,迭代其都将提供所需的操作,如*和++(有些累需要的操作可能比其他类多)。其次,每个容器类都有一个超微标记,当迭代其递增到超越容器的最后一个值后,这个值将被賦给迭代其。每个容器类都有begin()和end()方法,他们呢分别返回一个指向容器的第一个元素和超尾位置的迭代其。每个容器类都使用++操作,让迭代其从指向第一个元素逐步指向超微位置,从而便利容器中的每一个元素。
STL通过为每个类定义适当的迭代其,并以统一的风格射击类,能够对内部表示决然不同的容器,编写相同的代码。
使用C++11新增的自动类型推断可进一步简化:对于矢量或列表,都可使用如下代码:
for (auto pr = scores.begin(); pr != scores.end(); pr ++)
    cout << *pr << endl;
z实际上,作为一种变成风个,最好避免直接使用迭代其,而应尽可能使用STL函数(如for_each())来处理细节。也可以使用C++11新增的基于范围的for循环:
for (auto x : scores) cout << x << endl;
总结一下STL方法:首先是处理容器的方法,应尽可能用通用的属于来表达算法,使之独立于数据类型和容器类性。为使通用算法能够适用于具体情况,应定义能够满足算法需求的迭代其,并把要求加到容器设计上。即基于算法的要求,设计基本迭代其的特征和容器特征。

16.4.2 迭代器类型
STL定义了五种类型的迭代器:输入迭代器、输出迭代器、正向迭代器、双向迭代器、随机迭代器。

16.4.3 迭代器层次结构
……

16.4.4 概念、改进和模型
……

16.4.5 容器种类
以前的11个容器类型分别是:deque,list,queue,priority_queue,stack,vector,map,multimap,set,multiset,bitset
C++新增了forward_list,unordered_map,unordered_multimap,unordered_set,unordered_multiset,且不将bitset视为容器,而将其视为一种独立的类别。
【这里包含各个种类容器的介绍*】

《C++ Primer Plus》16.4 泛型编程 学习笔记的更多相关文章

  1. Ubuntu(16.04.2)学习笔记(一)如何解决dpkg: error processing install-info

    一.服务器安装软件是出现以下的错误信息: www@TinywanAliYun:~$ sudo apt-get install letsencrypt Reading package lists... ...

  2. C++ 11 从C++ primer第五版的学习笔记

    1. auto (page107) auto 推断会忽略const   const int ci = i, & cr = ci; auto b = ci; // b is an int (to ...

  3. C Primer Plus(第五版)学习笔记-可变宏:...和__VA_ARGS__

    一 .__VA_ARGS__ P454 所讲printf()这些输出函数的参数是可变的,在调试程序时,可能希望定义参数为可变的输出函数, 那么可变参数宏会是一个选择,例如: #define DEBUG ...

  4. 2016年3月16日Android学习笔记

    1.Jdk1.7以上switch语句中才能用字符串,在Android Studio中我改正了jdk的版本为1.8,但是还是出同样的错误,原来我用的sdk版本是4.4的,改成5的就没有问题了. 2.引入 ...

  5. C++学习笔记(3)

    本学习笔记是C++ primer plus(第六版)学习笔记.是C++学习笔记(2)的后续.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p ...

  6. C++学习笔记(2)

    本学习笔记是C++ primer plus(第六版)学习笔记.是C++学习笔记(1)的后续.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p ...

  7. C++学习笔记(1)

    本学习笔记是C++ primer plus(第六版)学习笔记.复习C++基础知识的可以瞄瞄. 转载请注明出处http://www.cnblogs.com/zrtqsk/p/3874148.html,谢 ...

  8. MongoDB学习笔记系列

    回到占占推荐博客索引 该来的总会来的,Ef,Redis,MVC甚至Sqlserver都有了自己的系列,MongoDB没有理由不去整理一下,这个系列都是平时在项目开发时总结出来的,希望可以为各位一些帮助 ...

  9. C++学习基础十六-- 函数学习笔记

    C++ Primer 第七章-函数学习笔记 一步一个脚印.循序渐进的学习. 一.参数传递 每次调用函数时,都会重新创建函数所有的形参,此时所传递的实参将会初始化对应的形参. 「如果形参是非引用类型,则 ...

随机推荐

  1. MySQL 数据库 varchar 到底可以存多少个汉字,多少个英文呢?我们来搞搞清楚

    一.关于UTF-8 UTF-8 Unicode Transformation Format-8bit.是用以解决国际上字符的一种多字节编码. 它对英文使用8位(即一个字节) ,中文使用24位(三个字节 ...

  2. linux系统下的缺失 libstdc++.so.6: version’GLIBCXX_3.4.20’的问题:

    在linux下遇到了一个这样的问题,使用anaconda时,它的安装库里的文件被提示:缺失 libstdc++.so.6: version’GLIBCXX_3.4.20’.后来经过查看资料知道了原来是 ...

  3. Git -- 相关命令

    git init : 将当前目录变成Git可以管理的仓库 git add :告诉Git,把文件添加到仓库 git commit -m "" :把文件提交到仓库 git status ...

  4. Python property,属性

    參考资料 http://www.ibm.com/developerworks/library/os-pythondescriptors/ 顾名思义,property用于生成一个属性.通过操作这个属性. ...

  5. WAS7.0安装补丁升级程序无法替换文件 java/docs/autorun.inf解决办法

    OS:Win7 64bit WAS版本:WASND_7.0_Windows_x64_C1G2JML.zip WAS补丁升级程序版本:7.0.0.13-WS-UPDI-WinAMD64 异常信息: Ca ...

  6. PHI 数据库简介

    PHI是一个致病菌的数据库,截止到2017年8月1号为止,最新的版本是4.3,数据库中收录了实验验证过的致病菌的信息,其中有176个来自动物的致病菌,227个来自植物的致病菌,3个来自真菌的致病菌; ...

  7. winform 应用log4net做日志记录到mysql

    1.nuget装log4net 2.nuget控件台装 mysql.data Install-Package mysql.data -version 6.8.3 (太高的版本用不了,切记) 3.修改a ...

  8. Java SE 8 并发增强

    1.原子值 java5开始,提供了一些原子操作的类,如AtomicInteger.AtomicLong等 这些类提供了诸如incrementAndGet这样的原子操作方法. 单数如果想进行复杂操作,则 ...

  9. [NSURL URLWithString:] returns nil

    You need to escape the non-ASCII characters in your hardcoded URL as well: //localisationName is a a ...

  10. Unity3D热更新LuaFramework入门实战

    http://blog.sina.com.cn/s/blog_6788cd880102w8qy.html http://www.manew.com/thread-91845-1-1.html http ...