• ###学习《C++ Primer》- 3


    点击查看Evernote原文

    #@author:       gr
    #@date:         2014-10-04
    #@email:        forgerui@gmail.com
    

    Part 3: STL泛型算法(第10章)

    一、算法永远不会执行容器的操作

    算法本身不会执行容器的操作,它们只会运行在迭代器上,执行迭代器的操作,使用迭代器,使算法更加通用。算法的编程假定:算法永远不会改变底层容器的大小。算法可能改变元素的值或移动元素,但永远不会直接添加或删除。

    还有一个inserter的迭代器,可以向容器中添加元素。

    二、只读算法

    accumulate进行累加操作,可以进行数值累加,也可以进行字符串相加(连接)。

    accumulate(c.cbegin(), c.cend(), string(""));
    

    equal比较两个容器的元素是否一致,前两个表示容器的范围,第三个表示第二个容器的首位置。要保证第二个序列至少和第一个序列一样长。

    equal(c1.cbegin(), c1.cend(), c2.cbegin());
    

    三、写容器元素的算法

    fillfill_n不改变容器的大小,所以要保证有足够的空间。

    vector<int> vec(10);
    fill(vec.begin(), vec.end(), 0);        //fill(begin, end, val);
    fill_n(vec.begin(), vec.size(), 0);     //fill_n(pos, n, val);
    vector<int> vec2;
    fill_n(vec2.begin(), 10, 0);            //出错,vec2是空的,无法写入10个元素
    

    back_inserter是插入迭代器,通过这个迭代器进行赋值时,系统会自动调用push_back函数。

    vector<int> vec;
    auto it = back_inserter(vec);
    //进行赋值,会自动调用push_back进行插入
    *it = 42;
    fill_n(back_inserter(vec), 10, 1);
    

    copy向目的地写入元素。

    copy(vec1.cbegin(), vec2.cend(), back_inserter(vec2));
    

    replace替换元素值。

    //把0替换为42
    replace(vec.begin(), vec.end(), 0, 42);
    

    replace_copy接受第三个迭代器,指出调整后序列的保存位置。

    replace_copy(vec.cbegin(), vec.cend(), back_inserter(vec2), 0, 42);
    

    四、重排容器元素的算法

    unique并没有真正删除重复的元素,只是将重复的元素排到最后,end()成员函数返回的迭代器位置不变,调用erase删除之后,才真正删除重复的元素。

    vector<string> words = {"fox", "the", "red", "jump"}
    //排序
    sort(words.begin(), words.end());
    //排列在范围的前部,返回不重复区域之后的一个位置迭代器
    auto end_unique = unique(words.begin(), words.end());
    //删除重复单词
    words.erase(end_unique, words.end());
    

    五、lambda表达式

    [lambda$$表达式的形式与函数类似,可以将其传递给算法调用。其一般形式如下: [capture list] (parameter list) -> return type { function body } 可以忽略参数列表和返回类型,但必须永远包含**捕获列表**和**函数体**。 auto f = [] {return 42;} ; //比较两个长度大小 [](const string& a, const string& b){ return a.size() < b.size(); } 下面利用这个`lambda`表达式传递给算法进行排序。 stable_sort(words.begin(), words.end(), [](const string& a, const string& b) { return a.size() < b.size(); } ); 使用捕获列表可以获取当前的局部变量,可以在`lambda`表达式中直接使用这个变量。 [sz](const string& a){ return a.size() >= sz; }; #### 六、变量的修改对lambda表达式的影响 **捕获的类型**:1.值捕获;2.引用捕获;3.隐式捕获。 值捕获:被捕获的值是在lambda创建时拷贝的,随后修改捕获的局部变量值不会影响到lambda中的值。 值捕获:引用捕获则与值捕获相反,修改的值会作用到lambda表达式内部。 size_t v1 =42; auto f1 = [v1]{return v1;} //值捕获 auto f2 = [&v1]{return v1;} //引用捕获 v1 = 0; auto j = f1(); //值捕获,结果为42 auto k = f2(); //引用捕获,结果为0 隐式捕获:让编译器根据`lambda`表达式中的代码推断我们需要使用哪些变量。为了表示是隐式捕获,在捕获列表中加入&或=。&表示引用捕获,=表示值捕获。 wc = find_if(words.begin(), words.end(), [=](const string& s){return s.size > sz;} ); #### 七、lambda表达式中修改变量值 一般lambda表达式不会改变值捕获中被拷贝变量的值,如果我们希望在lambda式中改变其值,则需要将表达式声明为mutable。 size_t v1 = 42; auto f = [v1] () mutable {return ++v1;}; v1 = 0; auto j = f(); //j为43 引用捕获是否能够修改取决于该引用是否是`const`类型。 size_t v1 = 42; auto f = [&v1]{return ++v1;}; //v1不是const v1 = 0; auto j = f2(); //j为1 #### 八、lambda表达式返回类型 一些表达式无须指定返回类型,因为可以根据条件运算符的类型推断出来。如果,需要为一个lambda表达式定义返回类型时,必须使用尾置返回类型。 transform(vi.begin(), vi.end(), vi.begin(), [](int i)-> int //指定返回类型为int { if (i < 0) return -i; else return i; }); #### 九、参数绑定 `bind`的一般形式: auto newCallable = bind(callable, arg_list); `arg_list`中的参数可能包含形如_n的名字,n是一个整数,这些参数是“点位符”,表示第几个参数:_1表示第一个参数,_2表示第二个参数。 find_if(words.begin(), words.end(), bind(check_size, _1, sz)); //将第二个参数绑定为sz 参数: auto g = bind(f, a, b, _2, c, _1); //g是一个有两个参数的可调用对象 g(X, Y); //调用g(X,Y)相当于f(a, b, Y, c, X); 重排参数: auto g = bind(f, _2, _1); g(X, Y); //调用g(X,Y)相当于f(Y, X),正好顺序相反 #### 十、插入迭代器 对插入迭代器进行赋值等于向容器中进行写入,系统会自动调用插入函数。`inserter`,`back_inserter`,`front_inserter`也都是迭代器,这些名字是原来迭代器的别名,原来的名称太长`o_0!`。 vector<int> vi = {1, 2, 3}; auto it = back_inserter(vi); //插入迭代器 *it = 4; //vi 等于 {1, 2, 3, 4} #### 十一、流迭代器 虽然`iostream`不是容器,但标准库定义了这些IO类型对象的迭代器,`istream_iterator`,`ostream_iterator`。 istream_iterator<int> in(cin), eof; int sum = accumulate(in, eof, 0); ostream_iterator<int> out_iter(cout, " "); //每个输出后面都加一个换行 *out_iter = sum; #### 十二、反向迭代器 反向迭代器在查寻最后出现的元素时候非常有效,但如果直接使用反向迭代器进行输出,顺序是反过来的,可以通过迭代器的`base()`成员函数使之变为正常迭代器。 auto rcomma = find(line.crbegin(), line.crend(), ','); //反向查找逗号 cout << string(line.crbegin(), recomma) << endl; //直接使用,会使输出反过来 cout << string(rcomma.base(), line.cend()) << endl; //使用base()变成普通迭代器 #### 十三、算法形参模式 一般算法都有基本的参数形式,理解这些模式,可以更专注于算法所做的操作上。一般会有如下四种形式: alg(begin, end, args); alg(begin, end, dest, args); alg(begin, end, begin2, args); alg(begin, end, begin2, end2, args); #### 十四、算法命名规范 1. 一些算法以重载的方式传递一个谓词 //unique的重载 unique(begin, end); unique(begin, end, comp); //传递一个比较函数,代替默认的<或== 2. _if版本的算法 //有对值进行操作的算法,和对谓词判定的算法 find(c.begin(), c.end(), val); find_if(c.begin(), c.end(), condi); //查找condition为真的元素 3. 区分拷贝元素版本与不拷贝版本 reverse(begin, end); //反转输入范围元素的顺序 reverse_copy(begin, end, dst); //逆序拷贝到dst,这里可以使用inserter]

  • 相关阅读:
    第19章_枚举:
    第14章_类型信息:
    第13章_字符串:
    纯css背景图自适应
    事务隔离机制_悲观锁_乐观锁
    hibernate 缓存
    list 和 iterate
    hibernate 性能优化之 1+N 问题
    QBC(Query By Criteria) QBE (Query By Example)
    HQL(Hibernate Query Language)
  • 原文地址:https://www.cnblogs.com/gr-nick/p/4069785.html
Copyright © 2020-2023  润新知