【C++学习五】STL库的应用

【C++学习五】STL库的应用文章目录初识C++之STL标准库1.C++STL的三大核心组件2.自定义函数与算法对容器实现操作3.基于自定义函数以及操作模板实现简易数字图像处理3.1图像灰度变换3.2图像二值化4.初识STL容器之:set集合5.初识STL容器之:map(关联容器)结语初识C++之STL标准库STL是StandardTemplateLibrary的缩写,中文译为“标准模板库”。STL是C++标准库的一部分。我们之前已经基本了解了C++中的模板templet,以及模板的作用。可以说,C

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

初识C++之 STL标准库

STL 是 Standard Template Library 的缩写,中文译为“标准模板库”。STL 是 C++ 标准库的一部分。

我们之前已经基本了解了C++中的模板templet,以及模板的作用。可以说,C++STL就是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈等。并且做到了数据结构和算法的分离(使用模板可以将一种算法的实现不局限于一种数据结构)。

1. C++STL的三大核心组件

C++ STL的核心主要包括以下三种组件:

  1. 容器(Containers)

    容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。不同的容器基于不同的数据结构

  2. 算法(Algorithms)

    算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。以此同时,多亏了C++Templet,算法的实现也独立于容器。

  3. 迭代器(iterators)

    迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。

除了上面所述的STL的三大核心以外,C++STL中的算法(Algorithms)还有一个特点,那就是,算法中某一步骤的实现方式可以通过于用户外部传参(传入一个自定义的函数)实现,大大增加了算法的多样性。

2. 自定义函数与算法对容器实现操作

举一个例子,相信大家都有用过Algorithms中的sort()排序算法,sort算法的最后一个参数由用户传入比较函数,sort()算法进而根据用户自定义的比较方式进行排序。这样一来我们就可以要求sort算法按从小到大或是从大到小的方式进行排序。除此之外,假如我们传入一个类,只要我们在比较函数中定义对这个类的排序是按照类中的哪个成员按照哪种方式进行比较,sort函数就可以理解我们的意图。而不需要重复定义多个sort算法。

接下来我将定义自己的算法和函数,结合容器和迭代器解决序列变换(如取反、平方、立方),像素变换(二值化、灰度拉伸)。

自定义操作函数(可以类比于sort()):

// 对于顺序表而言的运算(使用自定义运算函数 MyOperator)
template <class T, class MyOperator>
void transCalc(T a, T& b, int nNum, MyOperator op)
{ 
   
    for(int i=0;i<nNum;i++)
    { 
   
        b[i] = op(a[i]);
    }
}

// 对于链表而言的运算(使用自定义运算函数 MyOperator)
template <class inputIt, class outputIt, class MyOperator>
void transCalcT(inputIt begInput, inputIt endInput, outputIt begOutput, MyOperator op)
{ 
   
    for(;begInput!=endInput;begInput++,begOutput++){ 
   
        *begOutput = op(*begInput);
    }
}

自定义操作模板(可以类比于用户自定义的比较函数):

// 这里定义操作模板,可以自定义op操作:
//取反
template<class T>
T InvT(T a)
{ 
   
    return -a;
}
//平方
template<class T>
T SqrT(T a)
{ 
   
    return a*a;
}

// 类操作模板,二值化:
// 由于二值化除了传入变量本身还需要传入阈值,因此使用类来定义
template<class T>
class MyThreshold
{ 
   
public:
    int threshold;
    // n默认是128
    MyThreshold(int n=128):threshold(n){ 
   }
    // 重载操作符"()",一旦使用()传入参数就执行自定义内容:
    int operator()(T val)
    { 
   
        return val > threshold;
    }
};




//比较模板函数
template<class T>
bool MyCompare(T a, T b)
{ 
   
    return a > b;
}
//自定义比较模板类
template<class T>
class MyComp
{ 
   
public:
    int op;
    // 自定义比较
    MyComp(int n):op(n){ 
   }
    bool operator()(T a, T b)
    { 
   
        switch(op){ 
   
        case 0:
            return a == b;
            break;
        case 1:
            return a > b;
            break;
        case -1:
            return a < b;
            break;
        }
    }
};

打印函数,方便可视化:

// 打印函数
template <class T>
void outputCont(string strName, T beg, T end)
{ 
   
    cout<<strName;
    for(;beg!=end;beg++){ 
   
        cout<<*beg<<" ";
    }
    cout<<endl;
}

测试样例:

void test_mystl()
{ 
   
    const int N = 5;
    vector<int> a = { 
   3,5,4,1,2};
    vector<int> b(5);
    // 取反
    transCalc(a,b,N,InvT<int>);
    outputCont("Inv a:", b.begin(), b.end());
    // 取平方
    transCalc(a,b,N,SqrT<int>);
    outputCont("Sqr a:", b.begin(), b.end());
    // 二值化
    transCalc(a,b,N,MyThreshold<int>(2));
    outputCont("Sqr a:", b.begin(), b.end());
    // sort函数使用自定义排序方法
    sort(a.begin(), a.end(), MyCompare<int>);
    outputCont("Sort a by max:", a.begin(), a.end());
    // sort函数使用自定义排序类
    sort(a.begin(), a.end(), MyComp<int>(-1));
    outputCont("Sort a by min:", a.begin(), a.end());
}

测试结果:

请添加图片描述

3. 基于自定义函数以及操作模板实现简易数字图像处理

本篇博客的图像处理依赖于C++opencv开源算法包

首先定义一个操作模板函数:

// 对于图像而言的运算(使用自定义运算函数 MyOperator)
template <class MyOperator>
void transCalc(Mat &src, int w, int h, MyOperator op)
{ 
   
    for(int row=0;row<h;row++){ 
   
        for(int col=0;col<w;col++){ 
   
            // 图像操作
            src.at<uchar>(row, col) = op(src.at<uchar>(row, col));
        }
    }
}

3.1 图像灰度变换

// 类操作模板,灰度对数变换:
// 由于二值化除了传入变量本身还需要传入阈值,因此使用类来定义
template<class T>
class logTransform
{ 
   
public:
    int C;
    double Gamma;
    // n默认是128
    logTransform(int c=1, double gamma = 1.0):C(c),Gamma(gamma){ 
   }
    // 重载操作符"()",一旦使用()传入参数就执行自定义内容:
    int operator()(T val)
    { 
   
        float Val = float(val)/255;
        return 255*C*log(1+Val*(Gamma-1)) / log(Gamma);
    }
};

测试样例:

int main(int argc, char *argv[])
{ 
   
    // 打开灰度图像
    Mat img = cv::imread("C:\\Users\\S.E\\Desktop\\c++\\opencv_qt\\rice.png", 0);
    int w = img.cols;
    int h = img.rows;
    imshow("original", img);
    transCalc(img, w, h ,logTransform<uchar>(1, 0.01));
    imshow("gamma=0.01", img);
    waitKey(0);
    return 0;
}

测试结果:

请添加图片描述

3.2 图像二值化

// 类操作模板,二值化:
// 由于二值化除了传入变量本身还需要传入阈值,因此使用类来定义
template<class T>
class MyThreshold
{ 
   
public:
    int threshold;
    // n默认是128
    MyThreshold(int n=128):threshold(n){ 
   }
    // 重载操作符"()",一旦使用()传入参数就执行自定义内容:
    int operator()(T val)
    { 
   
       return (val > threshold)*255;
    }
};

测试样例:

int main(int argc, char *argv[])
{ 
   
    // 打开灰度图像
    Mat img = cv::imread("C:\\Users\\S.E\\Desktop\\c++\\opencv_qt\\rice.png", 0);
    int w = img.cols;
    int h = img.rows;
    imshow("original", img);
    transCalc(img, w, h ,MyThreshold<uchar>(128));
    imshow("Threshold=128", img);
    waitKey(0);
    return 0;

}

测试结果:

请添加图片描述

4. 初识STL容器之:set集合

set是c++stl标准库实现的一个容器,能够给予数据进行自动排序。其基本的数据结构基于红黑树,因此其在插入和删除的效率上会比一般的序列容器高,比如vector

接下来我们将以set为基础实现一个非常超级无敌简易的学生管理“系统”,方便大家更好的理解。

首先定义一条学生信息的基本组成,封装成一个类:

// 学生类
class studentInfo
{ 
   
public:
    int _strNo;     // 学号
    string _strName; // 姓名
    // 构造函数
    studentInfo(int strNo, string strName){ 
   
        _strNo = strNo;
        _strName =strName;
    }
    // 重载 << 输出
    friend ostream& operator<<(ostream& os, const studentInfo& info)
    { 
   
        os<<endl<<info._strNo<<" "<<info._strName;
        return os;
    }
    // 重载比较运算符(只比较学号)
    friend bool operator<(const studentInfo& info1, const studentInfo& info2)
    { 
   
        return info1._strNo<info2._strNo;
    }
};

接着我们定义一个管理学生信息的类,用集合set存储每条学生信息,并且定义一些管理学生信息的基本增删改查方法:

值得注意的是,在对容器使用for循环遍历时可以使用auto自动声明一个迭代器。

template <class T>
class students
{ 

public:
//存储学生信息
set<studentInfo> stuSet;
int stuNum = 0;
// 构造函数
template <class t>
students(t stud){ 

for(auto it:stud){ 

stuSet.insert(it);
stuNum ++;
}
}
// 增加一个元素
void add_single(T stu){ 

stuSet.insert(stu);
stuNum ++;
}
// 批量增加元素
template <class t>
bool add_batch(t stu){ 

for(auto it:stu){ 

stuSet.insert(it);
stuNum ++;
}
return true;
}
// 根据学号删除元素
bool del(int No){ 

for(auto it:stuSet){ 

if(it._strNo == No){ 

stuSet.erase(it);
stuNum --;
return true;
}
}
return true;
}
// 根据姓名删除元素
bool del(string Name){ 

for(auto it:stuSet){ 

if(!Name.compare(it._strName)){ 

stuSet.erase(it);
stuNum --;
return true;
}
}
return true;
}
// 根据学号查找姓名
string searchName(int No){ 

for(auto it:stuSet){ 

if(it._strNo == No){ 

return it._strName;
}
}
return "Not Found";
}
// 根据姓名查找学号
int searchNo(string Name){ 

for(auto it:stuSet){ 

if(!Name.compare(it._strName)){ 

return it._strNo;
}
}
return -1;
}
// 根据学号修改姓名
void update_no(int No, string afterName){ 

for(auto it:stuSet){ 

if(it._strNo==No){ 

stuSet.erase(it);
break;
}
}
stuSet.insert(studentInfo(No, afterName));
}
};

值得注意的是,set涉及查找并删除的实现方法中,若我们删除完set中的一条数据,应该直接return或者break退出查找的循环。这是因为在删除一条数据之后,set中的树形结构发生了改变,由于set内部的树遍历机制。导致erase 之后不能从那个起点再往回遍历,因此如果下一条数据正好在当前数据的父节点之上,往回遍历就会出现野指针的错误。即:

... ...
for(auto it:stuSet){ 

if(it._strNo==No){ 

stuSet.erase(it);
break;
}
}
... ...

测试样例:

void testStuSet()
{ 

vector<studentInfo> stu1;
stu1.push_back(studentInfo(11, "haotianY"));
stu1.push_back(studentInfo(13, "jinyuG"));
stu1.push_back(studentInfo(47, "jiawenL"));
vector<studentInfo> stu2;
stu2.push_back(studentInfo(41, "zhaohongH"));
stu2.push_back(studentInfo(19, "chenxiS"));
stu2.push_back(studentInfo(22, "yihaoW"));
stu2.push_back(studentInfo(49, "haohaoW"));
students<studentInfo> stuSet(stu1);
// 增
stuSet.add_batch(stu2);
stuSet.add_single(studentInfo(22, "xiangdongX"));
// 删
stuSet.del(22);
// 改
stuSet.update_no(47, "xiuwenL");  // 根据学号修改姓名
// 查
cout<<stuSet.searchName(49)<<endl;      // 根据学号查找姓名
cout<<stuSet.searchNo("haohaoW")<<endl; // 根据姓名查找学号
outputCont("student Set:\n", stuSet.stuSet.begin(), stuSet.stuSet.end());
}

测试结果:

请添加图片描述

5.初识STL容器之:map(关联容器)

map 是一个关联容器,它提供一对一的数据处理能力(其中第一个(first)称为键,第二个(second)称为值。不同键的值可以相同),由于这个特性,它能在我们处理一对一数据的时候,在编程上提供快速通道。同时,和set一样,map内部也是自动排序的。

map应用之:输入一个字符串,用map统计每个字符出现的次数并输出字符及对应的次数。

//输入一个字符串,用map统计每个字符出现的次数并输出字符及对应的次数
void countStr(string str){ 

map<char, int> count;
for(int i = 0;i<str.length();i++){ 

// 如果第一次出现就赋值为1
if(count.find(str[i])==count.end()){ 

count[str[i]] = 1;
}
// 否则++
else{ 
count[str[i]]++;}
}
// 打印每个单词出现的次数
for(auto i:count){ 

cout<<i.first<<": "<<i.second<<" ";
}
}

测试样例:

int main()
{ 

countStr("iuewfhjsdbfkrhedjskjloisgjipjdzzsvdfgkzlkjbhgvdvjj");
return 0;
}

测试结果:

请添加图片描述

结语

本次实验基于前一次实验介绍的模板templet以及C++标准库中的STL库,实现了自定义的算法及函数并进行了简易的数字图像处理。同时,通过学生信息管理以及统计字符串中每个字母出现的次数这两个例子初步了解了set和map的基本使用方法,至此,c++对我而言算是正式敞开了遮掩的大门,露出了些许微不足道的光亮。
更多的基于C++的应用将作为我本学期的课设呈现

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/181722.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 分布式爬虫部署[通俗易懂]

    分布式爬虫部署[通俗易懂]【服务器端】1.下载redis,RedisDesktopManagerredis。2.修改配置文件(找到redis下的redis.windows.conf双击打开,找到bind并修改为0.0.0.0,然后protected-mode“no”3.打开cmd命令行进入redis的安装目录,输入redis-server.exeredis.windows.conf回车,保持程序一直开着。如果不是这个界面,证明redis服务已经开启了,需要redis-clishutdow

    2022年10月26日
  • Android学习之Toast遇到的问题(Toast弹窗不显示,多次点击控件且执行一次弹窗)

    Android学习之Toast遇到的问题(Toast弹窗不显示,多次点击控件且执行一次弹窗)Android学习之Toast遇到的问题及解决办法问题一:代码无问题,Toast弹窗不显示最近在学习Toast对象的时候,用AndroidStudio模拟机去看样式时,弹窗竟然不显示,因为之前都是显示的突然就不显示了,上网搜了一哈找到解决办法–1–换一个模拟机去调试2–清除一下模拟机的缓存(这个挺好用,每次用着用着不显示弹窗的时候我就去清除一下缓存就可以了)**图解如下**找到AVDManager选择你使用的模拟机,在右边小三角里下拉列表中找到WipeDate清除缓存就好啦

  • jupyternotebook和pycharm的区别_jupyternotebook和pycharm的区别

    jupyternotebook和pycharm的区别_jupyternotebook和pycharm的区别在PyCharm环境中使用JupyterNotebook

  • 基于matlab的方差分析_方差分析结果怎么看

    基于matlab的方差分析_方差分析结果怎么看方差分析是英国统计学家R.A.Fisher在20世纪20年代提出的一种统计方法,它有着非常广泛的应用。在生产实践和科学研究中,经验要研究生产条件或实验条件的改变对产品的质量或产量的影响。如在农业生产中,需要考虑品种、施肥量、种植密度等因素对农作物收获量的影响;又如某产品在不同的地区、不同的时期、,采用不同的销售方式,其销售量是否有差异。在诸多影响因素中,哪些是主要的,哪些是次要的,以及主要因素处于

    2022年10月15日
  • linux ant 安装

    linux ant 安装1。下载    下载地址:http://ant.apache.org/bindownload.cgi 下载apache-ant-1.8.1-bin.tar.gz(当前最新版本),将该下载包拷贝到/usr/local下(随意了,找个地方就好)2。解压     cd /usr/local       tar -zxvf apache-ant-1.8.1-bin.tar.gz     解压后会在/usr

  • accept 函数_accept函数是阻塞的吗

    accept 函数_accept函数是阻塞的吗服务器要做的最普通的事情之一就是接受来自客户端的连接请求。在套接字上使用重叠I/O接受连接的惟一API就是AcceptEx()函数【注一】。有趣的是,通常的同步接受函数accept()的返回值是一个新的套接字,而AcceptEx()函数则需要另外一个套接字作为它的参数之一。这是因为AcceptEx()是一个重叠操作,所以你需要事先创建一个套接字(但不要绑定或连接它),并把这个套接字通过参数传给Acc

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号