泛型编程风格

编程过程中遇到一个大坑!调用size方法,返回的类型是size_t,而不是int!对size_t做减法运算简直是灾难!
下面是对0做减1后的结果!!!
在这里插入图片描述

、---------------------------------------------------------------------------------------------------------------------
原理说明:
标准模板库(STL)两大组件:容器(序列和关联)和泛型算法(用以操作这些容器类的函数)。标准模板库中都是一些模板类的定义,这使得我们可以直接使用这些模板类而无需自己定义模板类。
序列式容器为我们维护了一组元素值,在序列容器上更多的还是迭代操作。关联容器重在快速寻找元素值。map是key-value相关联的容器,通过key查找value。set仅有key。
泛型算法:作用于容器上的某种算法。所谓泛型:与容器类型无关,容器所存储的数据类型也无关。
为了达到第二个目的,使用模板函数就可以了。将数据类型抽象出来作为类型参数。

为了达到第一个目的,算法的参数不应该出现具体的容器类型,容器是一组元素的集合,虽然容器的类型不一样,但最本真还是用来存储对象,反正算法也是对元素进行操作,那么我们干嘛不直接对对象进行操作,参数可以用来访问对象,如此一来可以避免写容器类型。接着思考,函数参数需要能够访问到容器里所有的元素,仅靠一个变量能访问到所有容器所有元素,对于一个存储在一片连续空间的容器对象来说(比如vector、array),指针可以达到这个效果,传入函数的参数是容器第一个元素的地址值,对指针进行递增运算就可访问到所有元素。只要能够访问到所有的元素就已经达到我们的目的!我们需要牢牢记住,对容器进行任何操作都是作用在容器中的元素上!所以能够访问到容器元素并对其进行操作才是最底层的愿景!!!这也是我们解决算法能应用到不同容器的思路!获取容器里的所有元素!
补充指针运算:对指针的运算时考虑所存储数据类型的大小的!取下标运算对应到指针运算是*(指针+下标),二步操作!
在这里插入图片描述在这里插入图片描述可惜容器对象并不都是存储在一片存储空间的,比如list容器,它的元素存在离散的内存块中。以离散块中存的指针表示线性关系。指针的算术运算关系只能用在连续空间上,对这种离散块存储的元素就没辙了。他有自己获取容器元素的一套方法。难道就没有办法了吗?当然不是!为什么要让最底层的方法被直接使用呢?包括之前使用的指针,通过设计一个类,让类为我们获取容器元素,为这个类重载一系列普通指针所支持的操作:递增(指向容器的下一个元素)、解除引用(获取当前所指向的元素值),我们就把这个类当做指针来使用!
STL中具体是怎么实现的呢?为每一个容器定义一个iterator类,这个类根据容器特点实现比较、解除引用、递增运算。所有容器都提供begin()和end()来获取iterator对象:即容器里第一个元素的地址和最后一个元素的下一个地址值。
如何使用iterator类对象?类型需要给出容器类型和存储数据的类型
在这里插入图片描述
到这里我们已经能到达算法对任何容器类型和数据类型都无关,但是其实还是有些问题的,比如泛型函数find(),在函数体内需要依赖元素所定义的相等运算符的定义的,如果元素是自定义类型的,没有重载相等运算符或是我们不想要用已经实现判断元素相等的方法,该怎么办呢?强大的C++,在泛型算法为我们提供了第三个参数即函数对象或函数指针。

那么就进一步探讨一下泛型算法第三个参数函数对象吧!
为了增强函数的通用性,可采取的做法:增加参数、抽象类型。没错就是增加参数。我们希望能指定元素间的比较操作,那么就将比较操作参数化呀,我们将比较操作定义在函数中,因此便有了参数类型为函数指针或是函数对象,于是我们便可以在函数中调用该函数已达成我们的目的。
但是使用函数指针的话,函数的通用型还是不高,因为函数指针限制了函数返回类型和参数类型个数,只能传递满足条件的函数,并不能用于所有的函数。由此出现了函数对象的概念,即重载了运算符()的类,可通过类对象调用()方法,由于将函数定义为类型,故而在模板函数中,可使用类型参数来表示函数对象,这样一来可表示任何函数,只要传递的实参是实现了运算符()的类对象就可以。

除了自己设计函数对象外,标准库为我们提供了预定义的函数对象:算术运算、关系运算、逻辑运算。他们被定义在头文件functional中。
在这里插入图片描述在这里插入图片描述算法transform的用法:
在这里插入图片描述函数对象适配器:
函数对象需要两个参数,但是在泛型算法中我们只能提供一个参数,另一个参数是固定的,比如find_if()算法找到小于10的元素,这个10个固定的,比较每个元素和10的大小关系,与transform算法不同,另一个操作数通过迭代器指出,而这里的操作数是固定的!C++设计者们便提出了对于这种情况的解决方法,即函数对象适配器,能绑定函数对象的一个参数(可以看出对函数对象做了修改),使得二元参数的函数只再需要一个参数。函数对象适配器可人性化的为我们绑定第一个参数或是第二个参数,bind1st()或是bind2st()
在这里插入图片描述泛型算法中有许多的复制算法,实现的核心思想是,为目的容器分配足够大的空间,从原容器复制到目的容器,罪魁祸首是赋值运算符和迭代器的自增运算。复制空间要么就是太大浪费,要么就是太小。还是使用这些个复制算法,但我们事先不给目的容器分配足够的大小,该如何去做?

特殊的迭代器:
insert iterator
使用插入适配器!能对容器进行转化,使得将调用赋值运算符转为调用合适的插入成员函数并返回迭代器。这样就不用担心内存大小的问题。数组不支持插入运算,具体来说,有三种inset iterator:
在这里插入图片描述在这里插入图片描述iostream iterator:
输出输入流是同类型的数据时,可定义istream_iterator 和 ostream_iterator类对象,此时将输入输出流视为容器,而istream_iterator和ostream_iterator就是作用于容器的迭代器。
对于输出流,只需指明输出流的终端是什么,无需指明流的起始终止迭代器。
在这里插入图片描述
对于输入流,需要指明源输入是什么(键盘还是文件)
在这里插入图片描述在这里插入图片描述除了输入输出使用标准设备外,在初始哈迭代器对象时,可以改变第一个参数以使其关联到希望的流对象(cin、cout、ofstream对象、istream对象、fstream对象)。
在这里插入图片描述

到这里,泛型算法的设计可以告一段落了。我们已经实现了算法有数据类型无关、容器类型无关、与底层对元素比较操作无关。

容器共通操作:
size()、clear()、赋值、==、!=、empty()、begin()、end()、insert()、erase()。
接下来,详细说说容器。就从序列容器开始吧。将序列容器分为两大类:连续空间存储和离散空间存储。连续内存空间为代表的vector和deque容器类,都说是连续空间了,自然是支持随机访问。但是对于在内部插入删除元素就很没有效率。而vector和deque的区别在于,vector仅支持在尾部插入删除元素,deque除了在尾部进行插入删除操作外,还可在头部插入删除。因此有需要随机访问需求,考虑使用vector和deque。list实现了双向链表,有三个字段:value、front、back。对于实现插入删除效率极高,因为只需要修改指针,但是无法达到随机访问,只能顺序访问。list的迭代器是不支持偏移运算的!曾经在这里犯过错,对迭代器进行偏移运算。
在这里插入图片描述
容器初始化:
可以为空,之后调用insert()、push_back()、push_front()添加元素,动态管理内存。
可以指定大小,不给初始值时,将使用默认值为元素初始化。
使用迭代器初始化容器
利用已有的容器,调用复制构造函数
在这里插入图片描述
在这里插入图片描述
特殊的尾部头部插入、删除方法:
在这里插入图片描述insert():
1、在指定的位置之前,插入一个值
在这里插入图片描述
2、在指定的位置之前,插入指定个数的值
在这里插入图片描述
3、在指定的位置之前,插入一个区间所标识的值
在这里插入图片描述
4、在指定的位置之前,插入该类型的默认值
在这里插入图片描述
erase():
删除一个元素
删除区间内元素
在这里插入图片描述Map:
输入key/value的方法,key不存在则将key加入到map中,并将value设置为所属类型的默认值:map[key](=value)
map容器的begin()、end()返回的迭代器指向的是pair类型<first,second>,为了获取Pair中的元素值,c++为我们提供了first和second成员以获取键和值。
map容器的find()方法,返回的是指定key下指向Pair类型的迭代器,若不存在key值,返回end()迭代器。
map的count(),判断key是否存在于该map中,存在返回1,否则返回0。

Set:
一群keys的组合,key不能重复出现。(否则应使用multiset)默认情况下,set会根据类型的less than运算符进行排序,set在插入元素时,不能指定元素的位置,因为它会根据元素类型为其排序。

下面是个设计泛型算法的小例子:

#include "stdafx.h"
#include<iostream>
#include<iterator>
#include<vector>
#include<algorithm>
#include<array>
#include<list>
#include<functional>
#include<map>
#include<set>
#include<string>

using namespace std;
template<class InputIterator, class OutputIterator, class EleType, class Function>
OutputIterator fliter(InputIterator begin, InputIterator end, OutputIterator obegin, EleType val, Function f);



int main()
{
	vector<int > a = { 1,2,3,66,4,2222 };
	array<double,6>b = { 333.2,22,444,2,3,1 };
	list<double>c;
	fliter(a.begin(), a.end(), ostream_iterator<int >(cout, " "), 2, greater<int>());
	fliter(b.begin(), b.end(), back_inserter(c), 222, less<double>());
	for (auto x : c)
		cout <<x << " ";
	cout << endl;
	map<string, int> word_count;
	set<string> unwords{ "the","a" };
	string w;
	//cin >> w;
	getline(cin, w);
	while (w[0]!='\0')
	{
		if (unwords.count(w))
		{
			getline(cin, w);

			continue;
		}
		word_count[w]++;
		getline(cin, w);

	}
	for (auto x : word_count)
	{
		cout << x.first << " :" << x.second << endl;
	}
	cin.get();
    return 0;
}

template<class InputIterator, class OutputIterator, class EleType, class Function>
OutputIterator fliter(InputIterator begin, InputIterator end, OutputIterator obegin,EleType val, Function f)
{
	while((begin=find_if(begin,end,bind2nd(f,val)))!=end)
	{
		//cout << *begin << endl;
		*obegin++ = *begin++;
		
	}
	return obegin;
}

下面是个应用泛型算法的小例子:

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;

void grow(vector<int > &a,int v);
bool is_ele(vector <int> &a, int ele);
int main()
{
	vector<int > a = { 1,1 };
	cout<<is_ele(a, 33);
	cin.get();
    return 0;
}

void grow(vector<int > &a,int v)
{
	//a.push_back(22);
	while (a.back() < v)
	{
		a.push_back(a[a.size() - 1] + a[a.size() - 2]);
	}
}
bool is_ele(vector <int> &a, int ele)
{
	grow(a, ele);
	//for (auto x : a)
		//cout << x << " ";
	return binary_search(a.begin(), a.end(), ele);
	//return true;
}

//map中嵌套了vector
#include "stdafx.h"
#include<fstream>
#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;

void read(ifstream & in  ,map<string , vector <string>> & result);
bool query(map<string, vector <string>> & result, string family);
void display(map<string,vector<string>>& result);
int main()
{
	ifstream in("1.txt");
	map <string, vector<string>> res;
	read(in, res);
	display(res);
	//cout << endl;
	cout << "family name:";
	string fam;
	getline(cin, fam);
	query(res, fam);
	cin.get();
    return 0;
}

void read(ifstream & in, map<string, vector <string>> & result)
{
	string textline;
	string family;
	//vector<string> child;
	
	string::size_type pos=0,previous=0,count=0;
	while (getline(in, textline))
	{
		vector<string> child;
		pos = 0;
		previous = 0;
		count = 0;
	//	cout << textline << endl;
		while ((pos = textline.find_first_of(" ", pos)) != string::npos)
		{
			count = pos - previous;
			if (!previous)
			{
				family = textline.substr(previous, count);
			}
			else {
				child.push_back(textline.substr(previous, count));
			}
			previous = ++pos;
		}
		if (previous < textline.size())
		{
			child.push_back(textline.substr(previous, pos-previous  ));
		}
		if (result.count(family))
		{
			cout << "this family already exist!"<<endl;
		}
		else
		{
			result[family] = child;
		}
	}
}
bool query( map<string, vector <string>> & result, string family)
{
	
	if (result.count(family))
	{
		//cout << family << " :";
		for (auto x : result[family])
		{
			cout << x << " ";
		}
		cout << endl;
		return true;
	}
	else
		return false;
}

void display(map<string, vector<string>>& result)
{
	for (map<string, vector<string>>::iterator it = result.begin(); it != result.end(); it++)
	{
		cout << it->first << ": ";
		for (auto i = it->second.begin(); i != it->second.end(); i++)
			cout << *i << " ";
		cout << endl;
	}
}

#include "stdafx.h"
#include<vector>
#include<iterator>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;
class Even {
public:
	bool operator()(int x)
	{
		return x % 2;
	}
};
int main()
{
	ofstream out1("1.txt");
	ofstream out2("2.txt");
	vector<int> a;
	copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter(a));
	vector<int> ::iterator it=partition(a.begin(), a.end(), Even());
	copy(a.begin(), it, ostream_iterator<int>(out1, " "));
	copy(it, a.end(), ostream_iterator<int>(out2, "\n"));
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值