C++顺序型容器使用中一些注意的地方

顺序型容器使用中一些注意的地方

vector的push_back与emplace_back

  • 相对于push_backemplace_back在添加元素时不会进行额外的复制或者拷贝工作
  • 参考链接:http://en.cppreference.com/w/cpp/container/vector/emplace_back
  • 上面的特点是左值引用(&)右值引用(&&)造成的。C++11中引入的右值引用,主要有2个优点(https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/):
    • 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
    • 能够更简洁明确地定义泛型函数。
  • 代码

    class A
    {
    public:
        A(int val, int type) : val_(val), type_(type) 
        {
            cout << "I am being constructed." << endl;
        }
    
        A(A&& other) : val_(other.val_), type_(other.type_)
        {
            cout << "I am being moved.\n";
        }
    private:
        int val_;
        int type_;
    };
    
    
    void test()
    {
        cout << "push_back :" << endl;
        vector<A> vec1;
        vec1.push_back(A(1, 2));
    
        cout << "emplace_back :" << endl;
        vector<A> vec2;
        vec2.emplace_back(1, 2);
    }
    

forward_list删除符合条件的节点

  • forward_list(单向链表)中可以得到一个节点及其前驱节点,erase_after用于删除这个节点之后的节点,因此我们需要记录当前节点的前驱节点
  • 删除特定值时,其前驱节点的信息也会发生改变(iter指向下一个节点)
  • 代码:删除列表中所有的奇数

    void test()
    {
        forward_list<int> lst;
        for (int i = 0; i < 10;i++)
            lst.push_front(i);
    
        auto curr = lst.begin();
        auto prev = lst.before_begin();
        while (curr != lst.end())
        {
            if (*curr % 2 == 1)
            {
                curr = lst.erase_after( prev );
            }
            else
            {
                prev = curr;
                ++curr;
            }
        }
        for (auto it = lst.begin(); it != lst.end(); it++)
            cout << *it << endl;
    }
    

迭代器

  • 注意:在使用迭代器的时候,尽量不要对容器进行操作,除非十分清楚迭代器的动作或者操作,否则容器操作可能会使得迭代器变得无效。

泛型算法

概述

  • 泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作。因此:泛型算法永远不会改变底层容器的大小。但是一些特殊的迭代器(inserter)会改变底层容器。

一些常用的泛型算法

accumulate
  • accumulate(begin, end, val):第三个参数是和的初值,他的类型决定了函数中使用哪个加法运算符以及返回值的类型(因此必须要求第三个参数的类型实现了加法运算符操作)。
  • code

    void test()
    {
        vector<int> nums = {1,2,3,4};
        cout << accumulate( nums.begin(), nums.end(), 0 ) << endl;
        vector<string> strs = {"123", "456", "789"};
        //cout << accumulate(strs.begin(), strs.end(), "") << endl; // 报错,因为const char* 没有提供加法操作
        cout << accumulate(strs.begin(), strs.end(), string("")) << endl;
    }
    
fill
  • 将给定范围的迭代器内的内容填充为给定数据
  • code

    void test()
    {
        vector<int> nums = {1,2,3,4};
        fill(nums.begin(), nums.end(), 0);
        for (auto &n : nums)
            cout << n << endl;
        fill_n( nums.begin(), 2, 4 ); // 要保证填充的个数小于容器的size
        for (auto &n : nums)
            cout << n << endl;
    }
    
replace,replace_copy
  • replace会修改原始数组,而replace_copy不会修改原始数组,会将调整后的数组保存在另外一个地方
  • code

    void test()
    {
        vector<int> nums = {1,2,3,4, 1,2};
        replace( nums.begin(), nums.end(), 1, 23 ); // 替换
        cout << "nums : ";
        for (auto &num : nums)
            cout << num << " ";
        cout << endl;
        vector<int> cpy;
        replace_copy( nums.cbegin(), nums.cend(), back_inserter(cpy), 23, 44 ); //  不改变原始数组,而是添加到新的列表中去
        cout << "nums : ";
        for (auto &num : nums)
            cout << num << " ";
        cout << endl;
        cout << "cpy : ";
        for (auto &num : cpy)
            cout << num << " ";
        cout << endl;
    }
    
unique
  • 功能:可以与erase结合,去除容器中的连续重复元素
  • code

    // 输出一个迭代器范围内的数据,最后以换行结束
    template<typename iter>
    void printVec(iter &start, iter &end, ostream &os = cout, string &split = string(" "))
    {
        while (start != end)
        {
            os << *start << split;
            ++start;
        }
        os << endl;
    
    }
    void test()
    {
        vector<int> nums = {1,1,2,3,4,4, 1,2};
        //将唯一的元素放在前面,在这之后的元素都需要删除,这个只会删除相邻的重复元素,即不检查不相邻的元素的值是否相等
        auto it = unique(nums.begin(), nums.end());
        nums.erase( it, nums.end() );
        printVec( nums.begin(), nums.end() );
    }
    
lambda表达式
  • 一个lambda表达式表示一个可以调用的代码单元,可以将其理解为一个未命名的内联函数,对于有些较为短小的代码段可以使用这种方法
  • 形式:[](){}[]中主要是写lambda表达式内部需要使用的变量(),被称为捕获,如果是采用引用的方式,则是引用捕获;主要是传参;{}是函数功能实现部分。
  • code

    template<typename iter>
    void printVec(iter &start, iter &end, ostream &os = cout, string &split = string(" "))
    {
        while (start != end)
        {
            os << *start << split;
            ++start;
        }
        os << endl;
    
    }
    void test()
    {
        vector<int> nums = {1,1,2,3,4,4, 1,2};
        auto f = [](){return 42; };
        cout << f() << endl;
        std::sort(nums.begin(), nums.end(), [](const int& a, const int& b){return a > b; });
        printVec( nums.begin(), nums.end() );
        int base = 3;
        auto it = find_if(nums.begin(), nums.end(), [base](const int& a){ return a < base; }); // 返回第一个满足条件的元素所在的迭代器位置
        printVec( it, nums.end() );
    }
    
for_each
  • 对于容器中的每个元素,都进行特定的操作

  • code

    void test()
    {
        vector<int> nums = {1,1,2,3,4,4, 1,2};
        for_each(nums.begin(), nums.end(), [](const int& a){cout << a*5 << " "; });
        cout << endl;
    }
    
transform
  • 按照某种方法对元素进行变换,需要给定转换后存储的位置
  • code

    void test()
    {
        vector<int> nums = {1,1,2,3,4,4, -5,-6};
        vector<int> result;
        transform(nums.begin(), nums.end(), back_inserter(result), [](int a) {return a < 0 ? -a : a; });
        transform(nums.begin(), nums.end(), nums.begin(), [](int a) -> int {return a < 0 ? -a : a; });  // ->int是指定返回值类型
        for_each(result.begin(), result.end(), [](const int& a){cout << a << " "; });
        cout << endl;
        for_each(nums.begin(), nums.end(), [](const int& a){cout << a << " "; });
        cout << endl;
    }
    
参数绑定bind
  • bind:用占位符绑定数据,可以实现参数位置替换或者参数简化的功能
  • code

    void test()
    {
        vector<int> nums = {1,1,2,3,4,4, -5,-6};
        vector<int> result;
        std::sort( nums.begin(), nums.end(), less<int>() );
        std::for_each(nums.begin(), nums.end(), [](const int& a){std::cout << a << " "; });
        std::cout << endl;
    
        // 使用bind函数与占位符,将less中第二个参数与第一个参数位置对调,实现从大到小排序的效果。
        // 也可以直接绑定第几个参数为定值等,实现少于原始函数参数个数的运算
        std::sort(nums.begin(), nums.end(), bind(less<int>(), std::placeholders::_2, placeholders::_1));
    
        std::for_each(nums.begin(), nums.end(), [](const int& a){std::cout << a << " "; });
        std::cout << endl;
    
        // 固定第二个参数为3,表示找到一个小于3的数,并记录当前位置
        auto it = find_if(nums.begin(), nums.end(), bind(less<int>(), std::placeholders::_1, 3) );
        std::for_each(it, nums.end(), [](const int& a){std::cout << a << " "; });
        std::cout << endl;
    }
    
  • 注意事项:如果出现IntelliSense: "for_each" is ambiguous的问题,可以在函数前加上命名空间std::
back_inserter,front_inserter,inserter
  • back_inserter是创建一个使用push_back的迭代器
  • front_inserter是创建一个使用push_front的迭代器
  • ‘inserter’:接受2个参数,分别是容器与迭代器的一个位置iter,所有的元素都会被插入到这个iter之前(依次)
输入输出流迭代器
  • 输入输出流迭代器是可以从流中读取数据或者将数据写入流中
  • 代码

    void test()
    {
        vector<int> nums;
        vector<int> result;
        // 输入流迭代器
        istream_iterator<int> in_iter(cin);
        istream_iterator<int> eof; // istream尾部迭代器
    
        while (in_iter != eof) // ctrl+z结束输入
        {
            nums.push_back( *in_iter++ );
        }
    
        // 输出流迭代器
        ostream_iterator<int> os_iter(cout, " ");
        for (auto &e : nums)
            *os_iter++ = e;
        cout << endl;
    
        copy( nums.begin(), nums.end(), os_iter );
        cout << endl;
    }
    
一些泛型算法的特殊情况(list与forward)
  • listforward都是链表类型,无法做到随机访问迭代容器,因此在对这两种容器进行操作时(sort,remove,reverse,unique等),优先使用其成员函数的算法,而非泛型算法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

littletomatodonkey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值