IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 标准库 书籍学习记录笔记 第5章 -> 正文阅读

[C++知识库]C++ 标准库 书籍学习记录笔记 第5章

5.3 迭代器?

  • 前置式递增比后置式递增效率更高,因为后者需要一个额外的临时对象,因为他需要存储一个迭代器原本的位置并将其进行返还,因此最好使用++pos,而不是pos++;

5.3.1 关联式容器的运用实例

  • 修改map默认的递增的方式,将内部排序修改为 递减方式
  • ? ? std::map<int,int,std::greater<int>>map{};
  • greater是一个预先定义的仿函数
  • 关联式容器使用自己的默认规则进行元素位置的存放,因此相较于序列式容器取消了push_back()和push_front()函数
  • std::greater - C++中文 - API参考文档
#include <list>
#include <iostream>
#include <set>
#include <map>

int main(){
    std::map<int,int,std::greater<int>>map{};
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    map.insert(std::make_pair(3,3));
    map.insert(std::make_pair(4,4));
    for (auto temp=map.begin();temp!=map.end();++temp) {
        std::cout << temp->first << " " << temp->second << std::endl;
    }
}
  • multimap包含在map头文件内部,允许key和value是1对多的关系
#include <list>
#include <iostream>
#include <set>
#include <map>

int main(){
    std::multimap<int,int,std::greater<int>>map{};
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    map.insert(std::make_pair(3,3));
    map.insert(std::make_pair(4,4));
    for (auto temp=map.begin();temp!=map.end();++temp) {
        std::cout << temp->first << " " << temp->second << std::endl;
    }
}
  • 迭代器访问 元素的时候,可以使用 迭代器->first? 或者? (*迭代器).first 使用*解除引用,再对元素进行访问
  • 类名 变量名;将创建一个对象将其存储在栈上,使用 变量名.成员? 进行使用
  • 类名 变量名 = new 类名();??将创建一个对象将其存储在堆上,使用 变量名->成员? 进行使用
  • map 键值/实值 所形成的群集中,如果所有的键值是唯一的,将其作为一个关联式数组;这里想表达的意思是我们可以像使用数组一样进行元素的访问,数组通过数组下标访问对应的数值,map可以通过指定key,访问对应的value。通过map[key]访问对应的数值

5.3.2 迭代器分类

  • 双向迭代器 Bidirectional iterator:双向行进,采用递增进行前进运算,使用递减进行后退运算,list、set、multiset、map和undered_map 都使用这个迭代器
  • 随机存储迭代器Random access iterator:在双向迭代器的基础上还具备了随机访问的特性,因此可以对迭代器增加或者减少一个偏移量,处理迭代器之间的距离,或者使用<和>之类的relational相对关系的操作符来比较两个迭代器。vector、deque和string均采用此迭代器
  • 尽量使用 != 这个的适配性更好,但是如果pos位于了end的后面,发生错误很难发现
    for(pos = coll.begin();pos != coll.end();++pos){}operator !=
    for(pos = coll.begin();pos < coll.end();++pos){}//operator < Random access iterator

5.4 算法

  • 算法不是容器类别的成员函数,而是搭配迭代器使用的全局函数,采用的是泛型函数编程的思维,数据和操作进行分离

5.4.2 处理多个区间

  • copy将第一区间的数据拷贝到目标区域,第一区间的起点和第一区间的终点确定了第一区间的元素的数量,因此第二区间只需要提供一个起点即可。
  • 但是copy函数使用的是覆写操作,不是插入操作,因此需要第二区间具备足够的内存空间,如下例子所示,会产生未定义的行为
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

int main(){
    std::list<int>coll1;
    std::vector<int>coll2;

    for (int i = 1; i <= 9; ++i) {
        coll1.push_back(i);
    }

    std::copy(coll1.begin(),coll1.end(),coll2.begin());
}
  • 对上述错误代码的修改:1,确认目标区域具备足够的内存;2,采用insert iterator
    std::list<int>coll1;
    std::vector<int>coll2;

    for (int i = 1; i <= 9; ++i) {
        coll1.push_back(i);
    }
    coll2.resize(coll1.size());
    std::copy(coll1.begin(),coll1.end(),coll2.begin());

5.5 迭代器配接器

  • Insert iterator 安插型迭代器
  • Stream iterator 流迭代器
  • reverse iterator 逆向迭代器

Insert iterator 安插型迭代器

  • 采用安插方式而不是覆写的方式运作,解决目标区域不足的问题? 3种方式
  • Back inserters 安插在容器的最尾端:内部调用push_back() 采用追加的操作,只能适用于具备push_back()成员函数的容器中? 适用于vector list deque
  • ? ? std::copy(coll1.begin(),coll1.end(),std::back_inserter(coll2));
  • Front inserters 安插在容器最前端,内部调用的是push_front() ,这种操作会逆转被插元素的次序,比如插入1 再次插入 2,输出的时候2会在1 的前面打印输出? 这个适用于 list 和 deque
  • ? ? std::copy(coll1.begin(),coll1.end(),std::insert_iterator<>(coll2));
  • General inserters一般性安插器: 将元素插入到初始化时接受的第二个参数指向的前方。内部调用的是insert函数,并按照新值和新的位置作为参数。适用于关联式容器

?流迭代器

  • stream iterator是一种用来读取stream流的迭代器。提供了必要的抽象性,使得键盘的输入像一个群集,使得程序员可以从中读取内容,也可以把算法的输出重新导向某个文件或者屏幕
  • 代码没有跑通
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;
int main(){
    std::vector<std::string>coll{};
    copy(std::istream_iterator<string>(cin),
            istream_iterator<string>(),
         back_inserter(coll));

    sort(coll.begin(),coll.end());

    unique_copy(coll.begin(),coll.end(),
                ostream_iterator<string>(cout,"\n"));
}

5.5.3 reverse iterators 逆向迭代器

  • coll.rbegin()指向的是逆向遍历的起点,也就是先前的end()-1 的位置?

5.6.1 移除元素

  • ?remove移除元素,是使用删除元素位置之后的元素对删除位置上的元素进行替代,不会是将其所有的3全部删除
  • 但是群集末尾未被覆盖的元素原封不动,但是从逻辑层面上讲,他们已经不属于这个群集了
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;
int main(){
    std::list<int>coll{};
    for(int i=1;i<=6;i++){
        coll.push_back(i);
        coll.push_front(i);
    }

    std::cout << "pre: ";
    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
    std::cout << std::endl;

    std::remove(coll.begin(),coll.end(),3);

    std::cout << "post: ";
    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
    std::cout << std::endl;
}
  • pre: 6 5 4 3 2 1 1 2 3 4 5 6
  • post: 6 5 4 2 1 1 2 4 5 6 5 6?
  • 实际上 算法会产生一个新的终点,需要更新新的终点的位置,获得新的区间,也就是缩减元素之后的容器的大小,亦或是删除元素的个数

?改进如下

  • ? ? std::list<int>::iterator end = std::remove(coll.begin(),coll.end(),3);
  • 这个end也就是经过删除之后逻辑上新的终点
  • 或者获取徐亚删除的元素的个数,利用distance函数,返回两个迭代器之间的距离
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;
int main(){
    std::list<int>coll{};
    for(int i=1;i<=6;i++){
        coll.push_back(i);
        coll.push_front(i);
    }

    std::cout << "pre: ";
    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
    std::cout << std::endl;

    std::list<int>::iterator end = std::remove(coll.begin(),coll.end(),3);

    std::cout << "post: ";
    copy(coll.begin(),end,ostream_iterator<int>(cout," "));
    std::cout << std::endl;

    std::cout << "number of removed elements: "<< std::distance(end,coll.end()) << std::endl;
    coll.erase(end,coll.end());

    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
    std::cout << std::endl;
}

5.6.2 更易型算法和关联式容器

  • 更易型算法是指 移除、重排、修改元素的算法,这些算法不适用于关联型算法,因为这些操作会修改位置上的数值,破坏排序
  • 因此,关联式容器的所有迭代器都被声明为指向常量
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;
int main(){
    set<int>coll{};
    for (int i = 0; i <= 9; ++i) {
        coll.insert(i);
    }
    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
    std::cout << std::endl;
    int num = coll.erase(3);
    cout << "number os removed elements:" << num << std::endl;
    copy(coll.begin(),coll.end(),ostream_iterator<int>(cout," "));
}

5.6.3 算法 VS 成员函数

  • 如果容器提供了功能相似但是性能更佳的成员函数,优先使用成员函数
  • 成员函数 对其进行了优化,相较于函数功能相似的算法

  • 成员函数?remove(coll.begin(),coll.end(),4)
  • 算法:? ? coll.remove(4);
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;
int main(){
    list<int>coll{};
    for (int i = 1; i <= 6; ++i) {
        coll.push_front(i);
        coll.push_back(i);
    }
    
    coll.erase(remove(coll.begin(),coll.end(),3),coll.end());
    coll.remove(4);
}

5.8.1 以函数作为算法的参数

  • 算法接收用户定义的辅助性函数 ,在算法的内部被调用
  • for_each函数对coll的begin到end区间内的每一个元素都调用print函数进行输出
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;

void print(int elem){
    std::cout << elem << " ";
}
int main(){
    std::vector<int>coll{};
    for (int i = 1; i <= 9; ++i) {
        coll.push_back(i);
    }
    std::for_each(coll.begin(),coll.end(),print);
}
  • 算法以🌲种态度来面对辅助函数,1,将其视为可有可无;2,视为必要;
  • 利用他们指定搜寻的规则、排序的规则或者定义某种操作,从而将某个容器内 的元素转换到另外一个容器
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;

void print(int elem){
    std::cout << elem << " ";
}

int square(int value){
    return value*value;
}
int main(){

    std::set<int>coll{};
    std::vector<int>coll2{};
    for (int i = 1; i <= 9; ++i) {
        coll.insert(i);
    }
    std::transform(coll.begin(),coll.end(),std::back_inserter(coll2),square);
    std::for_each(coll2.begin(),coll2.end(),print);
}

5.8.2 判断式

  • 潘端师就是返还结果是布尔数值的函数,常用于指定排序准则和搜寻准则,可能有一个或者两个操作数
  • 判断式 对于相同的输入返还相同的输出结果,且程序执行不可以改变内部状态?

一元判断式

?二元判断式

?

5.9 仿函数

  • 泛型编程强大威力和存粹抽象的一个例证。仿函数就是行为像函数,比如定义一个对象,他具备类似函数的一些特性,就可以当做函数来使用;比如使用小括号传递参数,如果指望定义的对象也可以实现 使用小括号传递参数,比如定义operator()???
#include <list>
#include <iostream>
#include <set>
#include <map>
#include <string>
#include <vector>

using namespace std;



class X{
public:
    int operator()(int v1,int v2)const;
};
int X::operator()(int v1, int v2) const {
    std::cout << v1 << " "<< v2<< std::endl;
}
int main(){

    X fo;
    fo(4,5);
    fo.operator()(4,5);
}
class PrintInt{
public:
    void operator()(int elem)const{
        std::cout << elem << std::endl;
    }
};
int main(){
    std::vector<int>coll{};
    for (int i = 1; i <= 6; ++i) {
        coll.push_back(i);
    }

    std::for_each(coll.begin(),coll.end(),PrintInt());
}

?

void add10(int & elem){
    elem += 10;
}
int main(){
    std::vector<int>coll{};
    for (int i = 1; i <= 6; ++i) {
        coll.push_back(i);
    }
    for_each(coll.begin(),coll.end(),add10);

    std::for_each(coll.begin(),coll.end(),PrintInt());
}
  • 如果需要数个不同的固定数值? 而且他们在编译期 都已经确认,可以使用template?
  • 使用模板传参,需要在函数被调用之前将数值传递给函数?
  • ?具体的代码如下面所示,? ? for_each(coll.begin(),coll.end(),AddValue(10));这种方式,使用AddValue(10)生成一个AddValue的组件,并且赋予了初值为10,构造函数就会把10保存在成员theValue中,在for_each之内,针对coll的每一个元素调用 () ,实际上就是对传入的那个AddValue对象 暂时调用 operator()操作,并以容器的元素作为参数
class AddValue{
private:
    int theValue;
public:
    AddValue(int v):theValue(v){};
    void operator()(int& elem)const{
        elem += theValue;
    }
};
class PrintInt{
public:
    void operator()(int elem)const{
        std::cout << elem << " ";
    }
};
int main(){
    std::vector<int>coll{};
    for (int i = 1; i <= 6; ++i) {
        coll.push_back(i);
    }
    for_each(coll.begin(),coll.end(),AddValue(10));
    std::for_each(coll.begin(),coll.end(),PrintInt());

    std::cout << std::endl;

    for_each(coll.begin(),coll.end(),AddValue(*coll.begin()));
    std::for_each(coll.begin(),coll.end(),PrintInt());
}
  • ?? ? for_each(coll.begin(),coll.end(),AddValue(*coll.begin()+5));这种方式,传入的参数
  • 使用这个技术就可以实现先前所说的 一个函数,两种状态的问题,用两个不同的仿函数实现
class AddValue{
private:
    int theValue;
public:
    AddValue(int v):theValue(v){};
    void operator()(int& elem)const{
        elem += theValue;
    }
};
class PrintInt{
public:
    void operator()(int elem)const{
        std::cout << elem << " ";
    }
};
int main(){
    std::vector<int>coll{};
    for (int i = 1; i <= 6; ++i) {
        coll.push_back(i);
    }
    AddValue addx(4);
    for_each(coll.begin(),coll.end(),addx);
    std::for_each(coll.begin(),coll.end(),PrintInt());

    std::cout << std::endl;

    AddValue addy(5);
    for_each(coll.begin(),coll.end(),addy);
    std::for_each(coll.begin(),coll.end(),PrintInt());
}

?5.9.2 预先定义的仿函数

  • C++标准库 包含了一些预定义的仿函数 涵盖了部分的基础运算
  • 比如排序默认的排序是从小到大,也就是operator < 的缺省排序准则就是 less<>
  • 默认的 std::set<int>coll? ->? set<int,less<int>>coll
  • 反向排列?? set<int,greater<int>>coll

  • negate<int>()? 将传入的int数值设置为 负数
  • transform()算法 将第一群集的所有元素处理之后转移到第二群集,如果第二群集的起始地址是自身,就相当于对第一群集的元素进行操作覆盖
  • transform的另外一种形式,按照某种特定的运算,将两个群集内的元素处理之后将其结果写入到第三群集;如下所示,设定 第一群集、第二群集 将两个群集的元素进行计算,将结果写回到第三群集

?

class PrintInt{
public:
    void operator()(int elem)const{
        std::cout << elem << " ";
    }
};
int main(){
    std::set<int,std::greater<int>>coll1;
    std::deque<int>coll2;

    for (int i = 1; i <= 9; ++i) {
        coll1.insert(i);
    }
    std::for_each(coll1.begin(),coll1.end(),PrintInt());

    transform(coll1.begin(),coll1.end(), //source
              back_inserter(coll2), //destination
              bind2nd(multiplies<int>(),10)); //operation

    std::cout << std::endl;
    std::for_each(coll2.begin(),coll2.end(),PrintInt());

    //replace value equals to 70 with 42
    replace_if(coll2.begin(),coll2.end(), //range
               bind2nd(equal_to<int>(),70), //replace criterion
               42); //new value
    std::cout << std::endl;
    std::for_each(coll2.begin(),coll2.end(),PrintInt());

    //remove all elements with values less than 50
    coll2.erase(remove_if(coll2.begin(),coll2.end(), //range
                          bind2nd(less<int>(),50)), //remove criterion
                        coll2.end());
    std::cout << std::endl;
    std::for_each(coll2.begin(),coll2.end(),PrintInt());
  • ? ? transform(coll1.begin(),coll1.end(), //source
    ? ? ? ? ? ? ? back_inserter(coll2), //destination
    ? ? ? ? ? ? ? bind2nd(multiplies<int>(),10)); //operation
  • bind2nd 就是? 进行multiplies<int>()运算的时候,将源群集的元素作为第一参数,将10 作为第二参数

  • 所有的仿函数通常都声明为inline,一方面使用类似函数的表示法或者抽象性,一方面又可以获得出色的效能
  • 还有一些仿函数可以调用群集内的每个元素的成员函数?

?

?

?

?

  • 智能指针:是一种对象,有着类似指针的接口,但是内部实现了一些额外的检查和处理工作?

?

?

?

?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-11 18:37:21  更:2021-09-11 18:39:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 13:51:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计