[Essential C++ notes] Chapter 3 泛型编程风格

原书:Essential C++, Stanley B. Lippman, 电子工业出版社, 2013.
章节:第3章 泛型编程风格
环境:CLion + MinGW


  1. Standard Template Library(STL)主要由两种组件构成:
    (1) 容器,包括vector, list, set, map等类
    (2) 泛型算法, 包括find(), sort(), replace(), merge()等

  2. 顺序性容器:vector和list

    关联容器:set和map,map是键值对组合,key用户查找,value表示存储或取出的数据,set仅含有key,可对其进行查询操作

  3. 泛型算法,泛型指它们和想要操作的元素类型(int, double, string等)无关,和容器(vector, list, array等)也彼此独立

  4. 泛型算法通过function template技术使其与操作对象的类型相互独立

3.1 指针的算术运算
  1. find()函数, 传入任意类型的vector对象和该类型的一个值,若值在vector中,则返回指向该值的指针,否则返回nullptr

    // 原书中返回类型为type*, 而vec为const vector<type>的引用, 返回type*使程序可通过该指针改变vector的值, 这是不合理的, 故编译器报错, 应该返回const type*
    // 倘若想传入vector<type>的引用, 并返回type*, 使得外部程序可借助该指针修改vector的值, 但问题在于该函数无法处理const vector<type>类型
    template<typename type>
    const type* find(const vector<type> &vec, const type &value) {
        if(!vec)
            return nulltpr;
        for(int i = 0; i < vec.size(); ++i)
            if(vec[i] == value)
                return &vec[i];
        return nullptr;
    }
    

    (1) 原书中find()函数的返回类型为type*, 而vec为const vector<type>的引用, 返回type*使程序可通过该指针改变vector的值, 这是不合理的, 故编译器报错, 应该返回const type*

    (2) 在官网下载的source code, find()函数的返回类型已修改为const type*

  2. find()函数查找array中是否存在指定值,且传入参数无需声明array

    (1) 方法1,使用参数size指明array的长度

    template<typename type>
    const type* find(const type *p, int size, const type &value) {
        if(!p || size < 1)
            return nulltpr;
        for(int i = 0; i < size; ++i)
            if(p[i] == value)
                return &p[i];
        return nullptr;
    }
    

    除了使用p[i]访问数组元素, 也可直接采用*进行提领,两者等效

    template<typename type>
    const type* find(const type *p, int size, const type &value) {
        if(!p || size < 1)
            return nulltpr;
        for(int i = 0; i < size; ++p, ++i)	// 在条件中同时进行i和p的递增
            if(*p == value)
                return p;
        return nullptr;
    }
    

    (2) 方法2,使用指向末尾的指针代替size

    template<typename type>
    const type* find(const type *p_start, const type *p_end, const type &value) {
        if(!p_start || !p_end)
            return nullptr;
        for(; p_start != p_end; ++p_start)
            if(*p_start == value)
                return p_start;
        return nullptr;
    }
    

    该函数的调用

    int arr[] = {1, 2, 3};
    int val = 1;
    if(find(arr, arr + 3, val))		// 传入数组最后一个元素的下一个地址
    	cout << "Found." << endl;
    else
        cout << "Not found." << endl;
    

    (3) 应用于vector对象

    int arr[] = {1, 2, 3};
    vector<int> vec(arr, arr + 3);
    int val = 1;
    // 传入vec[vec.size()]的地址, 即vec最后一个元素的下一个地址
    if(find(&vec[0], &vec[vec.size()], val))	
    	cout << *find(&vec[0], &vec[vec.size()], val) << endl;
    else
    	cout << "Not found" << endl;
    
  3. 指针的算术运算

    Q: ++p为指针的算术运算,假如p的初始地址为1000,执行后结果为?

    A: 指针的算术运算会把指针所指的类型的大小考虑进去,假如p指向int类型,在int为4 bytes的机器上执行,则p的结果为1004

  4. begin和end函数

    (1) begin函数返回vector对象的首地址或0

    template <typename type>
    const type* begin(const std::vector<type> &vec){
        return vec.empty()? 0: &vec[0];
    }
    

    (2) end函数返回vector对象的首地址或0

    template <typename type>
    const type* end(const std::vector<type> &vec){
        return vec.empty()? 0: &vec[vec.size()];
    }
    

    (3) vector通过begin和end调用find函数

    int arr[] = {1, 2, 3};
    vector<int> vec(arr, arr + 3);
    int val = 1;
    if(find(begin(vec), end(vec), val))
    	cout << *find(begin(vec), end(vec), val) << endl;
    else
    	cout << "Not found" << endl;
    

    以上程序可在C++98编译运行,在C++11或更高版本中会发生编译错误,而Essential Cpp也是基于C++98编写的,故可能出现不兼容的情况

    error: no matching function for call to 'find(std::vector<int>::iterator, std::vector<int>::iterator, int&)'
    
3.2 了解Iterator(泛型指针)
  1. find()函数若想处理list等容器,无需重写,只要在底层指针的行为之上提供一层抽象,取代程序原本的“指针直接操作”的方式,并把底层指针的处理都放在抽象层中,程序员直接对抽象层操纵,即可在只提供一份find()函数的情况下,处理STL中的所有容器类

  2. 设计iterator class使得可以使用"和指针相同的语法"对其进行程序的编写,如

    // first和last皆为iterator class object, 可以对其进行*/!=/++等运算, 这些运算均由iterator class内相关的inline函数提供
    while(first != last){
    	cout << *first << ' ';
    	++first;
    }
    
  3. 标准容器中的iterator类

    (1) 标准容器中名为begin()的操作函数返回一个指向第一个元素的iterator,名为end()的操作函数返回一个指向最后一个元素的iterator

    for(iter = vec.begin(); iter!= vec.end(); ++vec)
    	cout << *iter << ' ';
    

    (2) 定义iterator时,需要提供给iterator的信息包括

    1) 迭代对象(某个容器)的类型,决定如何访问下一个元素

    2) iterator指向的元素的类型,决定iterator提领操作的返回值

    // 可能的定义形式, 但STL并非这样做
    iterator<vector, string> iter;
    

    (3) STL定义iterator的方式

    vector<string> vec;
    vector<string>::iterator iter = vec.begin();
    

    双冒号::表示iterator是位于string vector定义内的嵌套nested类型

  4. const_iterator类

    对于const容器应使用const_iterator进行遍历操作,其允许我们读取容器内的元素,但无法进行写入操作

    const vector<string> vec;
    const vector<string>::const_iterator iter = vec.begin();
    
  5. 使用iterator

    (1) 获取元素值, 提领操作*iter

    cout << *iter << endl;
    

    (2) 调用底部元素(如string)所提供的操作

    cout << iter->size() << endl;
    // 调用iter所指string的方法size(), 打印该string的长度 
    
  6. 重写find()函数使其支持一对指针,或一对指向某种容器的iterator

    template<typename iteratorType, typename elemType>
    iteratorType find(iteratorType first, iteratorType last, const elemType &value) {
        for(; first != last; ++first)
            if(value == *first)
                return first;
        return last;
    }
    

    测试程序

    const int arr_size = 3;
    int arr[arr_size] = {1,2,3};
    vector<int> vec(arr, arr + arr_size);
    list<int> li(arr, arr + arr_size);
    const int *pa = find(arr, arr + arr_size, 2);
    if(pa)
    	cout << *pa << endl;
    vector<int>::iterator iter;
    iter = find(vec.begin(), vec.end(), 2);
    if(iter != vec.end())
    	cout << *iter << endl;
    list<int>::iterator it;
    it = find(li.begin(), li.end(), 2);
    if(it != li.end())
    	cout << *it << endl;
    
  7. 若想在find()函数中赋予equality运算符不同的意义,可传入函数指针代替原来的equality运算符,或运用所谓的function object,这也是STL中find()函数所支持的

  8. 泛型算法

    总共有近75个,常用算法如下

    (1) 搜索算法: find(), count(), adjacent_find(), find_if(), count_if(), binary_search(), find_first_of()

    (2) 排序次序整理算法:merge(), partial_sort(), partition(), random_shuffle(), reverse(), rotate(), sort()

    (3) 复制删除替换算法:copy(), remove(), remove_if(), replace(), replace_if(), swap(), unique()

    (4) 关系算法:equal(), includes(), mismatch()

    (5) 生成质变算法:fill(), for_each(), generate(), transform()

    (6) 数值算法:accmulate(), adjacent_difference(), partial_sum(), inner_product()

    (7) 集合算法:set_union(), set_difference()

3.3 所有容器的共通操作
  1. 所有容器类以及string类的共通操作

    (1) equality==和inequality!=运算符, 返回true或false

    (2) assignment=运算符,将某个容器复制给另一个容器

    (3) empty()在容器无任何元素时返回true,反之返回false

    (4) size()返回容器内元素个数

    (5) clear()删除所有元素

    #include <iostream>
    #include <vector>
    using namespace std;
    enum state{equalVec, emptyVec};
    string information[] = {"equal vectors", "empty vector"};
    void compare(vector<int> &v1, vector<int> &v2);
    int main(){
        int arr[] = {1, 2, 3};
        vector<int> v1(arr, arr + 3);
        vector<int> v2;
        compare(v1, v2);
        return 0;
    }
    void compare(vector<int> &v1, vector<int> &v2) {
        if(v1 == v2){						// equality运算符
            cout << information[equalVec] << endl;
        }
        if(v1.empty() || v2.empty()){		// empty()
            cout << information[emptyVec] << endl;
        }
        vector<int> t;
        t = v1.size() > v2.size()? v1 : v2;	// assignment运算符
        for(int i = 0; i < t.size(); ++i)	// size()
            cout << t[i] << ' ';
        cout << endl;	
        t.clear();							// clear()
        cout << "The size of t is " << t.size() << endl;
        if(t.empty())
            cout << information[emptyVec] << endl;
    }
    

    (6) begin()返回一个iterator,指向容器的第一个元素

    (7) end()返回一个iterator,只想容器的最后一个元素的下一个位置

    int arr[] = {1, 2, 3};
    vector<int> vec(arr, arr + 3);
    vector<int>::iterator first = vec.begin();
    vector<int>::iterator last = vec.end();
    for(; first != last; ++first)
    	cout << *first << ' ';
    

    (8) insert()将单一或某个范围内的元素插入容器内

    (9) erase()将容器内的单一元素或某个范围内的元素删除

    insert()和erase()的具体行为随容器是顺序性容器或关联容器而有所不同

3.4 使用顺序性容器

顺序性容器用于维护一组排列有序、类型相同的元素

  1. vector/list/deque

    (1) vector以一块连续内存来存放元素,各元素被存储在距离起始点的固定偏移位置上,随机访问效率较高,但插入或删除缺乏效率

    (2) list以双向链接存储内容,每个元素包含value、back指针(指向前一个元素)、front指针(指向后一个元素),随机访问效率低,插入或删除效率高(只需设定有限元素的back和front指针即可)

    (3) deque已连续内存存储元素,对于最前端与末端的插入和删除操作,效率更高

  2. 顺序性容器的定义(五种方式)

    (1) 产生的容器

    list<int> ilist;
    vector<int> ivec;
    

    (2) 产生特定大小的容器,各元素被默认初始化

    list<int> ilist(1024);	// int类型默认值为0
    vector<int> ivec(1024);
    

    (3) 产生特定大小的容器,并为每个元素指定初值

    list<int> ilist(10, 2); // 10个元素均初始化为2
    vector<int> ivec(10, 2);
    

    (4) 通过一对iterator产生容器,这对iterator用于标示一整组作为初值的元素的范围

    int arr[] = {1, 2, 3};
    vector<int> vec(arr, arr + 2);
    list<int> ilist(arr, arr + 2);
    for(int i = 0; i < vec.size(); i++)
    	cout << vec[i] << ' ';
    

    (5) 根据某容器产生新容器,复制原容器的元素作为新容器的初值

    int arr[] = {1, 2, 3};
    vector<int> vec(arr, arr + 2);
    vector<int> vec1(vec);
    for(int i = 0; i < vec1.size(); i++)
    	cout << vec1[i] << ' ';
    
  3. 在容器两端的操作

    (1) push_back():在末端插入一个元素

    vector<int> vec;
    vec.push_back(1);
    

    (2) pop_back():删除最后一个元素,但不会返回元素值

    vec.pop_back();
    

    (3) list和deque还支持push_front()pop_front()

    ilist.push_front(1);	// 在首段插入一个元素
    ilist.pop_front();		// 删除第一个元素,但不会返回值		
    

    (4) front():读取最前端元素的值

    (5) back():读取末端元素的值

  4. 插入函数insert()

    (1) iterator insert(iterator pos, elemType value)

    将value插入pos之前,返回要给iterator指向被插入的元素

    int arr[] = {1, 2, 3};
    list<int> ilist(arr, arr + 3);
    list<int>::iterator it = ilist.begin();
    int ival = 12;
    while(it != ilist.end()){
        if(*it >= ival){
            ilist.insert(it, ival);		// 将ival插入it之前
            break;
        }
        ++it;
    }
    if(it == ilist.end())
        ilist.push_back(ival);
    // 结果为1 2 3 12
    

    (2) void insert(iterator pos, int count, elemType val)

    在pos之前插入count个元素,每个元素的值均为val

    int arr[] = {1, 2, 3};
    list<int> ilist(arr, arr + 3);
    list<int>::iterator it = ilist.begin();
    int ival = 12, cnt = 4;
    while(it != ilist.end()){
        if(*it >= ival){
            ilist.insert(it, cnt, ival);		// 将cnt个ival插入it之前
            break;
        }
        ++it;
    }
    if(it == ilist.end())
        ilist.insert(it, cnt, ival);
    // 结果为1 2 3 12 12 12 12
    

    (3) void insert(iterator1 pos, iterator2 first, iterator2 last)

    在pos之前插入[first, last)所标示的各个元素

    int arr[] = {1, 2, 3};
    list<int> ilist(1);
    ilist.insert(ilist.end(), arr, arr + 3);
    // 结果为0 1 2 3
    

    (4) void insert(iterator pos)

    在pos之前插入一个元素,初值为默认值

  5. 删除函数erase()

    (1) iterator erase(iterator pos)

    删除pos所指的元素,返回指向被删除元素的下一个位置的iterator

    int arr[] = {1, 2, 3};
    list<int> ilist(arr, arr + 3);
    cout << *ilist.erase(ilist.begin()) << endl;
    // ilist为2 3, 输出结果为2
    

    (2) iterator erase(iterator first, iterator last)

    删除[first, last)范围内的元素

    int arr[] = {1, 2, 3, 4, 5};
    list<int> ilist(arr, arr + 5);
    list<int>::iterator it1 = find(ilist.begin(), ilist.end(), 2);
    list<int>::iterator it2 = find(ilist.begin(), ilist.end(), 4);
    list<int>::iterator it = ilist.erase(it1, it2);
    cout << *it << endl;
    // ilist变为1 4 5,输出结果为4
    // 可能误认为ilist会变为1 5,应注意的是,删除的范围不包含last所指元素
    
3.5 使用泛型算法
  1. 使用泛型算法应包含algorithm头文件

    #include <algorithm>
    
  2. 常用泛型算法

    (1) find()

    用于搜索无序集合中是否存在某值,搜索范围由iterator[first, last)标出,若找到目标则返回一个iterator指向该值,否则返回一个iterator指向last

    (2) binary_search()

    用于有序集合的搜索,若找到就返回true,反之返回false。对于有序集合binary_serach()find()效率更高

    (3) count()

    返回数值相符的元素数目

    (4) serach()

    比对某个容器内是否存在某个子序列,若存在则返回一个iterator指向子序列起始处,反之返回iterator指向容器末尾

  3. 泛型算法的应用

    设计is_elem()函数判断val是否位于Fibonacci数列中

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    template<typename type> bool is_elem(type, vector<type>&);
    template<typename type> bool grow_vec(type val, vector<type> &vec);
    template<typename type> void display(type first, type last);
    int main(){
        vector<int> vec;
        int val[] = {34, 2, 4, 8, 12, 100};
        for(int i : val){
            if(is_elem(i, vec))
                cout << i << " is a member of Fibonacci sequence." << endl;
            else
                cout << i << " is not a member of Fibonacci sequence." << endl;
            display(vec.begin(), vec.end());
        }
    }
    template<typename type>
    bool is_elem(type val, vector<type> &vec){
        type max_value = vec.empty()? 0 : vec[vec.size() - 1];
        if(max_value < val)
            return grow_vec(val, vec);
        if(max_value == val)
            return true;
        return binary_search(vec.begin(), vec.end(), val);
    }
    template<typename type>
    bool grow_vec(type val, vector<type> &vec) {
        if(vec.size() < 2)
            vec.insert(vec.begin(), 2 - vec.size(), 1);
        while(vec[vec.size()-1] < val)
            vec.push_back(vec[vec.size()-1] + vec[vec.size() - 2]);
        return vec[vec.size() - 1] == val;
    }
    template<typename type>
    void display(type first, type last){
        for(; first != last; ++first)
            cout << *first << ' ';
        cout << endl;
    }
    

    若不确定传入的vector是否已经过排序,则可将容器复制一份进行排序再调用binary_search(),但实际上这使得复杂度由O(logN)(排序容器binary_serach算法的时间复杂度)变为O(NlogN)(sort算法的时间复杂度)

    vector<int> temp(vec.size());
    copy(vec.begin(), vec.end(), temp.begin());
    sort(temp.begin(), temp.end());
    binary_search(temp.begin(), temp.end(), val);
    
3.6 设计泛型算法
  1. 设计一个函数,接受用户传入的vector,返回新的vector内含原vector中小于10的所有数值

    // 方法1,只能判断小于10,缺乏弹性
    vector<int> less_than_10(const vector<int> vec){
        vector<int> re;
        for(int i : vec)
            if(i <= 10)
                re.push_back(i);
        return re;
    }
    
    // 方法2,可以判断小于x
    vector<int> less_than_x(const vector<int> vec, int x){
        vector<int> re;
        for(int i : vec)
            if(i <= x)
                re.push_back(i);
        return re;
    }
    

    允许用户指定不同的比较操作,如大于、小于等,可以用函数去掉原来的less-than运算符

    // 方法3,可以进行不同的比较判断
    #include <iostream>
    #include <vector>
    using namespace std;
    bool less_than(int a, int b);
    vector<int> filter(const vector<int> &vec, int x, bool(*compare)(int, int));
    void display(const vector<int> &vec);
    int main() {
        vector<int> vec(10, 1);
        vector<int> v1 = filter(vec, 2, less_than);
        vector<int> v2 = filter(vec, 0, great_than);
        display(v1);
        display(v2);
        return 0;
    }
    
    bool less_than(int a, int b){
        return a < b;
    }
    bool great_than(int a, int b){
        return a > b;
    }
    vector<int> filter(const vector<int> &vec, int x, bool(*compare)(int, int)){
        vector<int> re;
        for(int i : vec)
            if(compare(i, x))
                re.push_back(i);
        return re;
    }
    void display(const vector<int> &vec){
        for(auto p = vec.begin(); p != vec.end(); ++p)
            cout << *p << ' ';
        cout << endl;
    }
    // 运行结果
    // 1 1 1 1 1 1 1 1 1 1
    // 1 1 1 1 1 1 1 1 1 1
    

    用find()代替for循环

    // 找到每一个等于val的元素
    int count_occurs(const vector<int> &vec, int val){
        vector<int>::const_iterator iter = vec.begin();
        int cnt = 0;
        while((iter = find(iter, vec.end(), val)) != vec.end()){
            ++cnt;
            ++iter;		// iter不断增加保证每个元素只遍历一次
        }
        return cnt;
    }
    
  2. function object

    (1) function object是一种class的实例对象,这类class对function call运算符做了重载操作

    (2) STL定义了一组function object,分为算术运算、关系运算和逻辑运算

    1) 六个算术运算:plus<type>, minus<type>, negate<type>, multiplies<type>, divides<type>, modules<type>

    2) 六个关系运算:less<type>, less_equal<type>, greater<type>, greater_equal<type>, equal_to<type>, not_equal_to<type>

    3) 三个逻辑运算:logical_and<type>, logical_or<type>, logical_not<type>

    (3) function object的应用

    sort(vec.begin(), vec.end(), great<int>());
    // sort()默认使用less-than运算符,若传入great_than function object,vec中元素讲会以降序排列
    
  3. function object adapter

    (1) function object less<type>期望外界传入两个值,若第一个值小于第二个则返回true,但本例中每个元素都必须和用户指定的数值比较,只需传入一个值即可,这可以通过将第二个参数绑定至用户指定的数值完成

    (2) function object adapter提供了两个binder adapter,能使二元binary function object转化为一元unary function object

    1) bind1st将指定值绑定至第一个操作数

    2) bind2nd将指定值绑定至第二个操作数

    vector<int> filter(const vector<int> &vec, int x, less<int> lt){
        vector<int> re;
        vector<int>::const_iterator iter = vec.begin();
        while((iter = find_if(iter, vec.end(), bind2nd(lt, x))) != vec.end()){
            // bind2nd将lt的第二个参数绑定为x,返回lt传入find_if函数
            re.push_back(*iter);
            ++iter;
        }
        return re;
    }
    // find_if接受两个iterator,标示元素的范围,在范围内的元素若调用第三个参数指向函数返回值为true,则find_if返回一个iterator指向该元素
    
  4. 泛型化:消除filter()与容器的元素类型以及容器类型的依赖关系

    (1) 使用函数模板,将元素类型假如template的声明中

    (2) 传入一堆iterator标示原容器的始末,传入一个iterator指示从何处开始复制元素

    int main() {
        int arr[] = {1, 2, 3, 10, 4, 10, 5, 6, 10};
        vector<int> vec(arr, arr + 9);
        int arr1[9];
        vector<int> v(9);
        cout << "filtering integer array for values greater than 8" << endl;
        filter(arr, arr + 9, arr1, 8, greater<int>());
        cout << "filtering integer vector for values less than 8" << endl;
        filter(vec.begin(), vec.end(), v.begin(), 8, less<int>());
    }
    
    template <typename InputIterator, typename OutputIterator, typename ElemType, typename Comp>
    OutputIterator filter(InputIterator first, InputIterator last,
            OutputIterator at, const ElemType &val, Comp pred){
        while((first = find_if(first, last, bind2nd(pred, val))) != last){
            cout << "found value: " << *first << endl;
            *at = *first;
            ++at;
            ++first;
        }
        return at;
    }
    
  5. negator adapter

    对function object的真伪值取反,not1对unary funciton object的真伪值取反,not2对binary function object的真伪值取反

  6. 另一种找到容器内所有小于某值的元素的做法

    先对元素排序,以find_if()找到第一个大于指定值的元素位置,再删除该位置之后至vector末尾的所有元素

    (1) non-template版本

    vector<int> sub_vec(const vector<int> & vec, int val) {
        vector<int> tmp(vec);
        sort(tmp.begin(), tmp.end());
        auto iter = find_if(tmp.begin(), tmp.end(), bind2nd(greater_equal<int>(), val));
        tmp.erase(iter, tmp.end());
        return tmp;
    }
    

    (2) 泛型化版本

    template <typename InputIterator, typename OutputIterator, typename ElemType, typename Comp>
    OutputIterator sub_vec(InputIterator first, InputIterator last, OutputIterator at, ElemType val, Comp pred) {
        sort(first, last, pred);
        // 传入pred, 使sub_vec可支持less, greater, less_equal, greater_equal四种关系比较操作
        auto it = find_if(first, last, not1(bind2nd(pred, val)));
        // find_if需要将绑定val后的unary function object进行negator操作
        while(first != it)
            *at++ = *first++;
        return at;
    }
    // 测试程序
    int main(){
        int arr[] = {1, 2, 3, 10, 4, 10, 5, 6, 10};
        vector<int> vec(arr, arr + 9);
        vector<int> v(vec);
        v.erase(sub_vec(vec.begin(), vec.end(), v.begin(), 6, less_equal<int>()), v.end());
        display(v);
    }
    // 输出1 2 3 4 5 6
    
3.7 使用Map
  1. map是一对数值,其中key作为索引,value作为数据

  2. map的定义及使用

    #include <map>
    #include <string>
    
    map<string, int> words;
    // 统计输入字符串出现的次数
    string tword;
    while(cin >> tword)
    	words[tword]++;    // 若不在words中会先被放到map内并默认初始化为0
    // 打印所有字符串及其出现次数
    map<string, int>::iterator it = words.begin();
    for(; it != words.end(); ++it)
        cout << "keys: " << it->first << " value: " << it->second << endl;
    
  3. map对象有一个名为first的member对应于key,名为second的member对应于value

  4. 查找map内是否存在某个key的方法

    (1) 方法一:直接把key当作索引用

    if(words["c"] == 0)
    	cout << "c not in words" << endl;
    

    缺点:被查询的key若不在map内,则会被自动添加进map中,并初始化其value为0

    (2) 方法二:利用map的find()函数,将key传入

    if(words.find("c") == words.end())
    	cout << "c not in words" << endl;
    

    (3) 方法三:利用map的count()函数,将key传入

    if(words.count("a") == 0)
    	cout << "a not in words" << endl;
    

    任何一个key再map内最多只有一份,故count()函数只会返回0或1,而multimap可储存多份相同的key

3.8 使用Set
  1. Set的定义和使用

    int arr[] = {1, 3, 2, 2, 1, 4, 2, 3, 0};
    set<int> s(arr, arr + 9);		
    // 传入始末iterator初始化set, 默认按照less-than运算符进行排序
    auto iter = words.begin();
    for(; iter != words.end(); ++iter)
    	cout << *iter << ' ';
    cout << endl;
    // 输出0 1 2 3 4
    
  2. 使用set的insert()函数为set添加新元素

    (1) 添加单一元素, 使用单一参数的insert()

    int val = 2;
    s.insert(val);
    

    (2) 添加某个范围内的元素,使用双参数的insert()

    int arr[] = {1, 3, 2, 2, 1, 4, 2, 3, 0};
    s.insert(arr, arr + 9);
    
3.9 使用Iterator Inserter
  1. 先前对filter()的实现中,将源端容器符合条件的元素通过赋值的方式加入目的端容器中,通过定义和源端容器同样大小的目的端容器保证不会溢出

    while((first = find_if(first, last, bind2nd(pred, val))) != last){
        cout << "found value: " << *first << endl;
        *at++ = *first++;
    }
    

    缺点:大部分情况下,目的端容器的大小太大

  2. 与复制有关的泛型算法,copy(), copy_backwards()等,接受一个iterator,均使用赋值复制元素,此后iterator递增至下个位置,但都必须保证目的端容器足够大

  3. insertion adapter

    (1) **back_inserter()以容器的push_back()**函数取代assignment运算符,传入back_insert()的参数即容器本身

    vector<int> result_vec;
    unique_copy(ivec.begin(), ivec.end(), back_inserter(result_vec));
    

    (2) inserter()一容器的insert()函数取代assignment运算符,接受两个参数,一个是容器,另一个是itereator指向插入操作的起点

    vector<int> result_vec;
    unique_copy(ivec.begin(), ivec.end(), inserter(result_vec, result_vec.end()));
    

    (3) front_inserter()会以容器的push_front()函数取代assignment运算符,只适用于list和deque

    list<int> ilist_clone;
    copy(ilist.begin(), ilist.end(), front_inserter(ilist_clone));
    

    使用insertion adapter必须包含iterator头文件

    #include <iterator>
    
    template <typename InputIterator, typename OutputIterator, typename ElemType, typename Comp>
    OutputIterator filter(InputIterator first, InputIterator last,
            OutputIterator at, const ElemType &val, Comp pred){
        while((first = find_if(first, last, bind2nd(pred, val))) != last){
            cout << "found value: " << *first << endl;
            *at++ = *first++;	
            // 传入back_inserter()后,遇到赋值运算回自动地以容器的push_back()代替赋值运算符
        }
        return at;
    }
    int main() {
        int arr[] = {1, 2, 3, 10, 4, 10, 5, 6, 10};
        vector<int> vec(arr, arr + 9);	
        vector<int> v(1, 0);	// 目的端容器最初有一个元素,值为0
        filter(vec.begin(), vec.end(), back_inserter(v), 8, less<int>());
        // 调用filter时传入back_inserter(v)
        display(v);
    }
    // 输出结果为0 1 2 3 4 5 6
    
3.10 使用iostream Iterator
  1. 从标准输入设备读取一串string,将其存入vector中,并进行排序,最后将字符串写回标准输出设备

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    using namespace std;
    int main(){
        string word;
        vector<string> text;
        while(cin >> word)
            text.push_back(word);
        sort(word.begin(), word.end());
        for(string i : text)
            cout << i << ' ';
        cout << endl;
    }
    
  2. iostream iterator类,istream_iterator和ostream_iterator分别支持单一元素的读取和写入

    #include <iostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    int main(){
        vector<string> text;
    	istream_iterator<string> is(cin);	// 将is定义为绑定至标准输入设备的istream_iterator
    	istream_iterator<string> eof;		// 之要定义时不指定istream对象,即代表定义文件末尾EoF
        copy(is, eof, back_inserter(text));
        sort(text.begin(), text.end());
        ostream_iterator<string> os(cout, " ");	// 第二个参数为分隔符, 默认为''
        copy(text.begin(), text.end(), os);
    }
    
  3. 读写文件

    将istream_iterator绑定至ifstream object,将osteram_iterator绑定至ofstream object即可

    #include <string>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    #include <fstream>
    using namespace std;
    
    int main(){
        vector<string> text;
        ifstream infile("in.txt");
        ofstream outfile("out.txt");
        if(! infile){
            cerr << "file does not exist" << endl;
            return -1;
        }
        istream_iterator<string> is(infile);	// 将istream_iterator绑定至infile
        istream_iterator<string> eof;		
        copy(is, eof, back_inserter(text));
        sort(text.begin(), text.end());
        ostream_iterator<string> os(outfile, " ");	// outstream_iterator绑定至outfile
        copy(text.begin(), text.end(), os);
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值