C++ 泛型算法的基础理解

看了C++primer,在本篇文章中谈谈个人对泛型算法的理解,以及关于迭代器的知识和std库函数相关联的操作。

目录

一、标准库算法与容器

二、标准库算法分类

2.1 只读算法

2.2 写容器元素算法

2.3 插入迭代器的使用

三、一、二谓词   Lamda表达式  函数对象

3.1 一二谓词

 3.2 Lamda表达式

3.3 函数对象

4、bind()函数占位符     函数对象适配器

4.1 bind()函数

4.2 函数对象适配器


一、标准库算法与容器

学C++的人都知道标准库STL,包含容器、迭代器和算法三个部分,那么他们之间有啥关系呢?

其实C++标准库开发容器的是一部分人,开发算法的是一部分人,那两个部分如何关联起来呢?没错就是通过迭代器,他们之前制定了相应的规则,通过迭代器来使算法和容器能够相互结合。

列:假如有一个vector<int>容器和一个排序算法sort,可以通过迭代器使用容器运用现有的算法,结果是vecint=={1,2,3,4,5}但是要注意迭代器的位置。

vector<int> vecInt{ 1,4,2,3,5 };
sort(vecInt.begin(), vecInt.end());

注意迭代器end()指向最后一个元素的后一个位置。

当然标准库算法对于内置数组也是通用的,同容器一样,传入内置数组的地址就行,例如:

int nIntnum[]{ 1,3,4,2,5 };
sort(&nIntnum[0], &nIntnum[5]);
//sort(begin(nIntnum), end(nIntnum));

二、标准库算法分类

大多数算法都定义在头文件algorithm中。标准库还在头文件numeric中定义了一组数值泛型算法。

算法的一些通用性:

1.beg()和end()是表示元素范围的迭代器。几乎所有算法都对一个由beg()和end()表示的序列进行操作。当然也可以是小于(beg()和end()的一部分)这个范围的迭代器。dest是表示目的序列的迭代器。

2.beg2是表示第二个输入序列开始位置的迭代器。end2表示第二个序列的末尾位置(如果有的话)。如果没有end2,则假定beg2表示的序列至少与beg和end表示的序列一样大。beg和beg2的类型不必匹配,但是,必须保证对2个序列中的元素都可以执行特定操作或调用给定的可调用对象。

3.unaryPred和binaryPred是一元和二元谓词,分别接受1个和2个参数,都是来自输入序列的元素,两个谓词都返回可用作条件的类型,comp是一个二元谓词,满足关联容器对两个变量判断关系的要求

4.unaryOp和binaryOp是可调用对象(对象仿函数),可分别使用来自输入序列的1个和2个实参来调用。如果需要更多的需求,可以了解函数对象适配器。如下图所示,红色是比较重要的

 5.每个算法都提供两个重载的版本,第一个版本使用底层类型的操作规则;第二个版本使用自己的函数

2.1 只读算法

查找算法    返回满足条件的第一个元素迭代器 否则返回end

  • find(beg, end, val)
  • find_if(beg, end, unaryPred)
  • find_if_not(beg, end, unaryPred)
  • adjacent_find(beg, end)             相邻元素查找,
  • adjacent_find(beg, end, binaryPred)
  • search_n(beg, end, count, val)   从此位置开始有count个相等元素 
  • search_n(beg, end, count, val, binaryPred)
  • binary_search(beg, end, val)  (二分查找)返回一个bool值,指出序列中是否有val的元素,不提供位置且序列必须已经排序
  • binary_search(beg, end, val, comp)
     

查找子序列    返回满足条件的第一个元素迭代器 否则返回end

  • search(beg1, end1, beg2, end2) 查找str2在str1中首次出现的位置
  • search(beg1, end1, beg2, end2, binaryPred)
  • find_first_of(beg1, end1, beg2, end2)  查找str2范围中任意元素在str1中首次出现的位置
  • find_first_of(beg1, end1, beg2, end2, binaryPred)
  • find_end(beg1, end1, beg2, end2)   类似search,返回的是最后一次出现的位置。如果第二个输入范围为空,未找到,返回end迭代器
  • find_end(beg1, end1, beg2, end2, binaryPred)

查找最大最小值

  • min_element(beg, end) 
  • max_element(beg, end, comp)
  • minmax_element(beg, end) 返回pair

统计算法  返回int

  • count(beg, end, val)
  • count_if(beg, end, unaryPred) 

累加/串接算法 返回容器元素相应的类型

  • accumulate(beg, end, va);   若为int相当于val+sum(容器原数) 若为string相当于val+str1+str2+...(容器原数)

比较算法 返回bool

  • equal(beg1, end1,beg2)用千确定两个序列是否保存相同的值。相同返回true,不同false,容器2至少和容器一元素  一样多。

2.2 写容器元素算法

改变容器指定位置元素,不增删移动;注意迭代器不执行容器操作

  • fill(beg, end, val)                    将所有元素置val
  • fill_n(beg,n.val)                      将n个元素置val      算法不检查写操作 
  • fill_n(beg,vec.size(),val)=fill(vec.beg,vec.end,val)
  • copy(beg1,end1,beg2)           将序列1中的元素拷贝给序列2   算法不检查写操作
  • replace(beg, end, 5, 4)           将所有的5替换为4   
  • replace_copy(vec3.begin(), vec3.end(), vec4.begin(), 0, 5);//不改变原容器,创建一份拷贝。
  • merge(beg1, end1, beg2, end2, dest) 合并且两个输入序列必须都是有序的,默认<运算符
  • merge(beg1, end1, beg2, end2, dest, comp)
  • sort(beg, end)       排序要求随机访问迭代器,list有自己的sort函数,queue和stack不支持
  • unique(beg, end)   重排序列,,返回一个指向指向不重复元素的尾后位置迭代器,对相邻的重复元素,通过sort+unique覆盖它们来进行”删除“,

  • move(beg, end, dest)  使用std::move,将其移动到迭代器dest开始的序列中,对临时变量,以及不再使用的变量而言

2.3 插入迭代器的使用

  • back_inserter()尾后插入迭代器
  • inserter(vecstr, dest);//在des迭代器t前插入
  • front_inserter()头部插入迭代器
auto backiter = back_inserter(vecstr);
backiter = "13555";

 在vecstr尾部插入字符串"13555"

auto initer = inserter(vecstr, vecstr.begin());//在dest前插入
initer = "initer";

在vecstr.begin的位置前插入字符串"initer"

auto frontiter = front_inserter(liststr);//插入头部,若是一个数组会,则会把把数组序列颠倒
frontiter = "13555";

 在vecstr.begin的头部前插入字符串"13555""

三、一、二谓词   Lamda表达式  函数对象

3.1 一二谓词

如果函数对象或者普通函数接受一个参数,并且返回值是Bool,叫一元谓词

如果函数对象或者普通函数接受二个参数,并且返回值是Bool,叫二元谓词

示例:

//一元谓词
bool csometo3(int a){
	return  a > 3;
}
//二元谓词
bool cmp(int a, int b){
	return  a < b;
}

 3.2 Lamda表达式

[capture list] (params list) -> return type {function body} 

[capture list] (params list) {function body} 

[capture list] {function body}

示例:

[=](const string &str) {
		return str.size() > nsize1;
	}
[](int num) ->bool{
		return num > 3;
	}
//可变lambda,单一return语句,会推断返回类型
auto lambda1 = [&, nsize1]() mutable { return ++nsize1; };//lambda对象保存nsize1的拷贝值,并且可改变,复合捕获
nsize1 = 0;
auto j = lambda1();
auto lambda2 = [=, &nsize1]() mutable { return nsize1++; };//lambda对象引用size1的值,复合捕获
auto m = lambda2();
//可变lambda,非单一return语句,会返回void,指定返回类型
auto lambda3 = [&, nsize1]()mutable ->int {
nsize1++;
return nsize1; };
int bsize = lambda3();

 Lamda[]捕获形式 说明

[] 不捕获任何外部变量

[变量名, …] 默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符)

[this] 以值的形式捕获this指针

[=] 以值的形式捕获所有外部变量

[&] 以引用形式捕获所有外部变量

[=, &x] 变量x以引用形式捕获,其余变量以传值形式捕获

[&, x] 变量x以值的形式捕获,其余变量以引用形式捕获

[a]()mutable {} 使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量

auto lambda = [nsize1]()  { return ++nsize1; };//错误使用,不允许改变nsize1值
auto lambda = [nsize1]() mutable { return ++nsize1; };//正确使用,不允许改变nsize1值

3.3 函数对象

如果函数对象接受一个参数,那么就叫 一元函数对象

如果函数对象接受两个参数,那么叫 二元函数对象

函数对象与函数指针相比,有两个优点:第一是编译器可以内联执行函数对象的调用;第二是函数对象内部可以保持状态。函数对象也称仿函数。比如内部添加变量进行计数和某些变量的保存等。

class Mycompare
{
public:
	bool operator()(int a,int b) {
		return  a < b;
	}
};

template<class T>
class Myprint {
public:
	void operator()(T str) {
		cout << str << " ";
	}
};

//使用函数对象
int numInt[]{1,2,7,9,45,12,5};
sort(begin(numInt), end(numInt), Mycompare());
cout << "sort()_numInt:" << endl;
for_each(begin(numInt), end(numInt), Myprint<int>());

四、bind()函数占位符     函数对象适配器

4.1 bind()函数

placeholders ,占位符。表示新的函数对象中参数的位置。当调用新的函数对象时,新函数对象会调用被调用函数,并且其参数会传递到被调用函数参数列表中持有与新函数对象中位置对应的占位符。

示例:

int add(int x1, int x2, int x3, int x4)
{
	return x1 + x2 + x3 + x4;
}

//bind函数,不是占位符的参数默认为拷贝,如nsize、j
using namespace std::placeholders;//_i命名空间
//对于某些对象和类,不能拷贝的,原函数为引用,则bind中相应参数使用ref(),c++primer357页
auto funadd = bind(add, ref(nsize), _2, _1, j);//绑定函数并使用占位符
int total = funadd(10, 20);//等价于add(nsize,20,10,j)

4.2 函数对象适配器

可以通过适配器将两个参数的函数转换成一个参数的函数。stl中实现了几个不同用途的适配器

  • bind1st bind2nd 绑定适配器函数          和函数bind有点类似,各有优缺点
  • not1 not2 取反适配器

  • ptr_func 函数指针适配器

  • mem_fun mem_func_ref 成员函数适配器

示例:

//函数对象适配器
cout << "函数对象适配器bind2nd:" << endl;
for_each (begin(numInt), end(numInt), bind2nd(myprint01(), 10));
cout << "函数对象适配器not2:" << endl;
sort(begin(numInt), end(numInt), not2(compare01()));
for_each(begin(numInt), end(numInt), bind2nd(myprint01(), 10));
for_each(begin(numInt), end(numInt), bind2nd(ptr_fun(cmp), 10));

 

以上内容若有不对的地方,还请在评论区中指出,谢谢!最后附上完整的代码:

// Mico alogrithm.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <list>
#include <algorithm>//泛型算法头文件
#include <numeric>//部分算法函数头文件
#include <iterator>//迭代器头文件
#include <functional>//bind函数头文件
using namespace std;
#pragma region  谓词函数

bool csometo3(int a)
{
	return  a > 3;
}

bool cmp(int a, int b)
{
	return  a < b;
}

bool isshorter(const string& str1, const string &str2)
{
	return str1.size() < str2.size();
}

void elimDpus(vector<string>& vecstr)
{
	sort(vecstr.begin(), vecstr.end());
	auto iter3 = unique(vecstr.begin(), vecstr.end());
	vecstr.erase(iter3, vecstr.end());
}

int add(int x1, int x2, int x3, int x4)
{
	return x1 + x2 + x3 + x4;
}

class Mycompare
{
public:
	bool operator()(int a, int b) {
		return  a < b;
	}
};
template<class T>
class Myprint {
public:
	void operator()(T str) {
		cout << str << " ";
	}
};

struct myprint01 : public binary_function<int, int, void> {   //二元函数对象 所以需要继承 binary_fucntion<参数类型,参数类型,返回值类型>
	void operator()(int v1, int v2) const {
		cout << v1 + v2 << " ";
	}
};
struct compare01 :public binary_function<int, int, bool> {
	bool operator()(int a, int b) const {
		return  a < b;
	}
};
#pragma endregion
int main()
{
#pragma region 迭代器与算法
	//迭代器与算法
	vector<int> vecInt{ 1,4,2,3,5 };
	sort(vecInt.begin(), vecInt.end());
	int nIntnum[]{ 1,3,4,2,5 };
	sort(&nIntnum[0], &nIntnum[5]);
	//sort(begin(nIntnum), end(nIntnum));
#pragma endregion

	//算法分类

#pragma region 只读泛型算法
	string str("jd31kahhlkf");
	vector<string> vecstr{ "umdf","123","main","err45","main","std","123" };
	string searchstr = "main";
	int num[]{ 1,2,3,4,5,6,7,8,8 };
	//find
	auto result1 = find(vecInt.begin(), vecInt.end(), 4);
	auto result2 = find(str.begin(), str.end(), 'a');
	int *result3 = find(begin(num), end(num), 6);
	int *result5 = find(num + 1, num + 4, 6);
	vector<int>::iterator result6 = find_if(vecInt.begin(), vecInt.end(), csometo3);
	auto result7 = find_if(vecInt.begin(), vecInt.end(), [](int num) ->bool {
		return num > 3;
	});
	auto result8 = find_if_not(vecInt.begin(), vecInt.end(), [](int num) ->bool {
		return num > 3;
	});
	//count
	int nsize = count(str.begin(), str.end(), 'k');
	int pos1 = count_if(str.begin(), str.end(), [](char cr) ->bool {
		return cr > 'a';
	});
	//accumulate
	int sum = accumulate(vecInt.begin(), vecInt.end(), 0);
	string sum1 = accumulate(vecstr.cbegin(), vecstr.cend(), string("23"));
	//equal
	auto compar = equal(str.begin(), str.end(), begin(num));//仅所有元素相等才返回true
	auto posadjacent1 = adjacent_find(str.begin(), str.end());
	auto posadjacent2 = adjacent_find(str.begin(), str.end(), [](char c1, char c2) ->bool {
		return c1 == c2;
	});
	//search
	auto possearch = search(sum1.begin(), sum1.end(), searchstr.begin(), searchstr.end());
	auto possearch1 = find_first_of(sum1.begin(), sum1.end(), searchstr.begin(), searchstr.end());
	auto possearch2 = find_end(sum1.begin(), sum1.end(), searchstr.begin(), searchstr.end());
	//for_each
	for_each(vecstr.begin(), vecstr.end(), [](const string &str) {
		cout << str << " ";
	});
#pragma endregion

#pragma region 写容器元素算法
	//改变容器指定位置元素,不增删移动
	//注意迭代器不执行容器操作
	fill(vecInt.begin(), vecInt.begin() + vecInt.size() / 2, 12);
	fill_n(vecInt.begin() + vecInt.size() / 2, 2, 8);
	//改变容器指定位置元素,结合插入迭代器(迭代适配器)增删
	vector<int> vec2;
	list<string> liststr;
	auto iter = back_inserter(vec2);//vector不支持front_inserter
	fill_n(iter, 1, 2);
	auto iter1 = back_inserter(vec2);
	fill_n(iter1, 2, 5);
	auto iter2 = front_inserter(liststr);
	fill_n(iter2, 10, "jjfsdghjkl");
	//拷贝算法
	vector<int> vec3(8, 0);
	vector<int> vec4(8, 0);
	vec4.reserve(16);//重新分配空间,至少容纳n个元素,改变容器的容量,而不是增加Size!!!
	copy(vec2.begin(), vec2.end(), vec3.begin());//vec3的数目大于等于vec2;
	replace(vec2.begin(), vec2.end(), 5, 4);//改变原容器
	replace_copy(vec3.begin(), vec3.end(), vec4.begin(), 0, 5);//不改变原容器,创建一份拷贝。
	//排序
	elimDpus(vecstr);
#pragma endregion	

#pragma region 谓词 Lamda 函数对象
	//谓词 Lamda
	//find_if只接受一元谓词,故用正则表达式lambda,传递参数nsize1,下为不可变lambda
	int nsize1 = 3;
	auto iter3 = find_if(vecstr.begin(), vecstr.end(), [=](const string &str) {
		return str.size() > nsize1;
	});
	//使用二元谓词 Lamda
	//stable_sort(vecstr.begin(), vecstr.end(), isshorter);
	stable_sort(vecstr.begin(), vecstr.end(), [](const string& str1, const string &str2)
	{
		return str1.size() < str2.size();
	});
	//输出str
	cout << "vecstr:" << endl;
	for_each(iter3, vecstr.end(), [](const string &str) {
		cout << str << " ";
	});
	//使用函数对象
	int numInt[]{ 1,2,7,9,45,12,5 };
	sort(begin(numInt), end(numInt), Mycompare());
	cout << "sort()_numInt:" << endl;
	for_each(begin(numInt), end(numInt), Myprint<int>());
	//auto lambda = [nsize1]()  { return ++nsize1; };//错误使用,不允许改变nsize1值

	//可变lambda,单一return语句,会推断返回类型
	auto lambda1 = [&, nsize1]() mutable { return ++nsize1; };//lambda对象保存nsize1的拷贝值,并且可改变,复合捕获
	nsize1 = 0;
	auto j = lambda1();
	auto lambda2 = [=, &nsize1]() mutable { return nsize1++; };//lambda对象引用size1的值,复合捕获
	auto m = lambda2();

	//可变lambda,非单一return语句,会返回void,指定返回类型
	auto lambda3 = [&, nsize1]()mutable ->int {
		nsize1++;
		return nsize1; };
	int bsize = lambda3();

	//bind函数,不是占位符的参数默认为拷贝,如nsize、j
	using namespace std::placeholders;//_i命名空间
	auto funadd = bind(add, ref(nsize), _2, _1, j);//绑定函数并使用占位符
	int total = funadd(10, 20);//等价于add(nsize,20,10,j)
	//对于某些对象和类,不能拷贝的,原函数为引用,则bind中相应参数使用ref(),c++primer357页

	//函数对象适配器
	cout << "函数对象适配器bind2nd:" << endl;
	for_each (begin(numInt), end(numInt), bind2nd(myprint01(), 10));
	cout << "函数对象适配器not2:" << endl;
	sort(begin(numInt), end(numInt), not2(compare01()));
	for_each(begin(numInt), end(numInt), bind2nd(myprint01(), 10));
	for_each(begin(numInt), end(numInt), bind2nd(ptr_fun(cmp), 10));
#pragma endregion
	/***************除了iterotor,其他迭代器类型*******************************/
	//三种插入迭代器
	auto backiter = back_inserter(vecstr);
	backiter = "13555";
	auto initer = inserter(vecstr, vecstr.begin());//在dest前插入
	initer = "initer";
	auto frontiter = front_inserter(liststr);//插入头部,若是一个数组会,则会把把数组序列颠倒
	frontiter = "13555";
	//iostream迭代器
	istringstream is("dfgdf");
	fstream in("C:\\Users\\he\\Documents\\111.vsdx");
	istream_iterator<string> intiter(in);
	istream_iterator<string> int_eof;//尾后迭代器,感觉为空
	vector<string> vecstr1(intiter, int_eof);//与下面循环类似
   /* while (intiter!=int_eof)
	{
		vecstr1.push_back(*intiter++);
	}*/
	istream_iterator<string> stros(is);
	istream_iterator<string> eof;
	vector<string> vecstr2(stros, eof);//与下面循环类似
	ostringstream os;
	ostream_iterator<string> ositer(os);
	for (auto &iter : vecstr2)
	{
		//*ositer++ = iter;
		ositer = iter;//效果与上面一致,类似插入迭代器
	}
	//反向迭代器,注意与普通迭代器的关系,可用来反调整数据顺序
	//find与_if的版本finfd_if第三个参数接受一个谓词,用来重载函数;
	auto pos = find_if(vecInt.begin(), vecInt.end(), [](const int& nnumber) {
		return nnumber > 8;
	});
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值