C++:STL应用

一、撰写自己的算法和函数,结合容器和迭代器解决序列变换(如取反、平方、立方),像素变换(二值化、灰度拉伸)

1. 一般的函数方法

首先是简单的取反和求平方的实现。

//取反(简单的方法)
void transInv(int a[], int b[], int nNum)
{
	for (int i = 0; i < nNum; i++)
	{
		b[i] = -a[i];
	}
}
//求平方(简单的方法)
void transSqr(int a[], int b[], int nNum)
{
	for (int i = 0; i < nNum; i++)
	{
		b[i] = a[i] * a[i];
	}
}

然后是输出的函数

//输出的函数模板
template < typename T>
void outputCont(string strNme, ostream& os, T begin, T end)
{
	os << strNme << ":";
	for (; begin != end; begin++)
	{
		os << *begin << " ";
	}
	os << endl;
}

接着是测试函数

void Test()
{
	const int N = 5;
	int a[N] = { 1,2,4,3,5 };
	//输出a
	outputCont("a", cout, a, a + N);
	//存储变换后的数组
	int b[N];
	//对数组中的元素取反
	transInv(a, b, N);
	outputCont("Inv a", cout, b, b + N);
	//对数组中的元素取求平方
	transSqr(a, b, N);
	outputCont("Sqr a", cout, b, b + N);
}

运行结果如下,输出了三个结果,分别是数组a,对数组a取反存储在数组b输出,对数组a求平方存储在数组b输出。
在这里插入图片描述

2. 模板函数

上述方法显然只能针对int类型,所以我们可以运用模板函数,针对不同 的数据类型,代码如下(只举例取反的模板函数,求平方等等同理):

//对数组取反的函数模板(开始使用模板)
template<typename T>
void transInvT(T a[], T b[], int nNum)
{
	for (int i = 0; i < nNum; i++)
	{
		b[i] = -a[i];
	}
}

接下来测试

	//对数组元素取反(使用模板)
	transInvT(a, b, N);
	outputCont("Inv a T", cout, b, b + N);

运行结果如下:
在这里插入图片描述

3. 结合容器和迭代器

在这里插入图片描述
上图是STL的基本组件及他们之间的联系。

3.1. 容器

什么是容器

  • 在C++中,容器被定义为:在数据存储上,有一种对象类型,它可以持有其他对象或指向其他对象的指针,这种对象类型就叫做容器。简单理解,即容器就是保存其他对象的对象。而且,这种“对象”还有处理“其他对象”的方法。
  • 容器是随着面向对象语言的诞生而提出的,它甚至被认为是早期面向对象语言的基础。现在几乎所有面向对象语言中都伴随着一个容器,C++中则是标准模版库(STL)。
  • C++采用基于模版的方式处理容器,STL中的容器提供了多种数据结构。

容器的优点

  • 容器类是一种对特定代码重用问题的良好的解决方案。
  • 可以自行扩展。当不知道需要存储多少对象时,就不知道应当开辟多大内存空间,而容器不需要预先设定空间长度,只需要创建一个对象并合理调用其提供的方法,其余的细节则由它自身完成,它自己申请内存或释放内存,并使用最优算法执行所有命令。
  • 容器类自动申请和释放内存,因此无需进行new和delete操作。

通用容器的分配
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2. 迭代器

我们使用容器的时候,迭代器是一个不可分割的部分。迭代器在STL中用来将算法和容器联系起来,起着一种胶着剂的作用。迭代器是一种检查容器内元素并遍历元素的数据类型。迭代器是一种行为类似指针的对象,它提供类似指针的功能,对容器成员的内容进行访问。
在这里插入图片描述
迭代器分类
在这里插入图片描述

3.3. 结合容器和迭代器解决序列变换

transInvT算法

template <typename inputIter, typename outputIter, typename MyOperator>
void transInvT(inputIter begInput, inputIter endInput,
			   outputIter begOutPut, MyOperator op)
{
	for (; begInput != endInput; begInput++, begOutPut++)
	{
		 //*begOutPut = ‐ (*begInput); 
		//改为函数的形式,就不需要对取反、平方等每一个算法重写一个函数
		*begOutPut = op(*begInput); 
	}
}

测试函数

void Test()
{
	//TestMap();
	//TestSet();
	//TestVector();

	const int N = 5;
	int a[N] = { 1,2,4,3,5 };
	//输出a
	outputCont("a", cout, a, a + N);

	//存储变换后的数组
	int b[N];
	//Vector(向量)是一个封装了动态大小数组的顺序容器(Sequence Container),跟任意其它类型容器一样,
	//它能够存放各种类型的数据。可以简单的认为,向量是一个能够存放任意类型的动态数组
	vector<double> vb(N);
	vector<double> vc(N);
	//对数组中的元素取反
	transInv(a, b, N);
	outputCont("Inv a", cout, b, b + N);
	//对数组中的元素取求平方
	transSqr(a, b, N);
	outputCont("Sqr a", cout, b, b + N);
	//对数组元素取反(使用模板)
	transInvT(a, b, N);
	outputCont("Inv a T", cout, b, b + N);

	transInvT(a, a + N, b);
	transInvT(a, a + N, vb.begin());
	transInvT(a, a + N, b, InvT<int>);
	transInvT(a, a + N, vb.begin(), InvT<int>);
	outputCont("Inv a by iter", cout, vb.begin(), vb.end());
}

运行结果
在这里插入图片描述

分析
transInvT算法顺序遍历begInput和endInput两个迭代器所指向的元素,将每个元素的值作为函数对象op的参数,然后将op的返回值通过迭代器begOutPut顺序输出,遍历完成后begOutPut迭代器指向的是输出的最后一个元素的下一个位置,即通过迭代器将数组a中的值经过函数对象op之后,把返回值通过迭代器顺序输出存入容器vector vb(N),最后通过outputCont输出vb。
很显然这样的模式,代码的复用率极高。

3.4. 结合容器和迭代器解决像素变换

template<typename T>
class MyThreshold {
public:
	//带参构造函数,后面的则是初始化,这样的初始化方式效率比较高
	MyThreshold(int n = 128) : _nThreshold(n)
	{

	}
	int operator()(T val)
	{
		return val < _nThreshold ? 0 : 1;
	}
	int _nThreshold;
};

测试函数

	transInvT(a, a + N, vb.begin(), MyThreshold<int>(2));
	outputCont("Inv a by treshold", cout, vb.begin(), vb.end());

运行结果
在这里插入图片描述
分析
MyThreshold(2)设定阈值为2,则1会变为0大于等于2的都会输出1。

3.5. 算法和函数之间的关系

在这里插入图片描述
我们 先来看看代码及测试结果:
mycomp排序方法

template < typename T>
bool mycomp(T a, T b)
{
	return a > b;
}

void Test()
{
	const int N = 5;
	int a[N] = { 1,2,4,3,5 };
	//输出a
	outputCont("a", cout, a, a + N);

	//模板函数
	sort(a, a + N, mycomp<int>);
	outputCont("a sorted", cout, a, a + N);
}

在这里插入图片描述
库函数functional中定义了类greater,greater():函数对象类greater中的定义了调用操作符()
在这里插入图片描述


void Test()
{
	const int N = 5;
	int a[N] = { 1,2,4,3,5 };
	//输出a
	outputCont("a", cout, a, a + N);

	//模板类,greater不是一个函数而是一个类所以要加括号
	sort(a, a + N, greater<int>());
	outputCont("a sorted", cout, a, a + N);
}

操作符()的实现中把>改成<结果为升序。
在这里插入图片描述
MyCompc():函数对象类MyCompc中的定义了调用操作符()

template < typename T>
class MyCompC
{
public:
	bool operator()(const T& x, const T& y) const
	{
		return	x > y;
	}
};
void Test()
{
	const int N = 5;
	int a[N] = { 1,2,4,3,5 };
	//输出a
	outputCont("a", cout, a, a + N);

	sort(a, a + N, MyCompC<int>());
	outputCont("a sorted", cout, a, a + N);
}

操作符()的实现中把>改成<结果为升序。
在这里插入图片描述
  sort(一个排序的算法),该算法中参数可以是两个,也可以是三个,第一个是要排序的数组的起始地址,第二个是结束的地址(最后一位要排序元素的后一位的地址,第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是升序排序。它使用的排序方法是类似于快排的方法,时间复杂度为O( n*log2(n) )。
  其中第三个参数既可以是函数也可以是函数的类的对象,但是必须带有具有双目运算符的比较函数即操作符(),且返回类型为bool。

二、用set存储学生信息,并进行增删改查操作

集合用来存储一组无重复的元素。由于集合的元素本身是有序的,可以高效地查找指定元素,也可以方便地得到指定大小范围的元素在容器中所处的区间。

首先构建studentInfo类,其中有一个构造函数,学号和姓名两个变量,还有两个对运算符的重载,一个是为了输出,一个是为了比较学号的大小以便于排序。

class studentInfo {
public:
	studentInfo(string strNo, string strName) {
		_strNo = strNo;
		_strName = strName;
	}
	string _strNo; 
	string _strName;
	//实现一个输出格式,不然不知道如何输出结构体
	friend ostream& operator<<(ostream& os, const studentInfo& info)
	{
		os << info._strNo << " " << info._strName;
		return os;
	}
	//实现比较的方式
	friend bool operator<(const studentInfo& info1, const studentInfo& info2) {
		return info1._strNo < info2._strNo;

	}
};

接着用容器vector创建一个students对象,将学生信息存储在vector当中,再通过遍历vector将其存储在set当中。

void TestSet()
{
	vector<studentInfo> students;
	students.push_back(studentInfo("10021", "Zhang san"));
	students.push_back(studentInfo("10002", "Li si"));
	students.push_back(studentInfo("10003", "Wang wu"));
	students.push_back(studentInfo("10011", "Wang Liu"));
	students.push_back(studentInfo("10010", "Wu Liu"));
	set<studentInfo> studentSet(students.begin(), students.end());
	outputCont("student set", cout, studentSet.begin(), studentSet.end());
}

运行结果如下,由于集合的性质,集合本身的元素是有序的,我们可以看到学生信息已经按升序的方式进行了排序。
在这里插入图片描述
增删改查

  • 增:insert(a) 插入某个元素,因为set有序,所以怎么插入无所谓
  • 删:erase(it) 该函数用于根据元素的值或元素在集合中的迭代器位置来擦除它
  • 改:set的迭代器it有const修饰符,那么对它元素的修改就必然不能成功了。
  • 查:find用于检查元素是否属于集合,如果元素在集合容器中找到,则返回指向该元素的迭代器。否则返回set.end()
void TestSet()
{
	vector<studentInfo> students;
	students.push_back(studentInfo("10021", "Zhang san"));
	students.push_back(studentInfo("10002", "Li si"));
	students.push_back(studentInfo("10003", "Wang wu"));
	students.push_back(studentInfo("10011", "Wang Liu"));
	students.push_back(studentInfo("10010", "Wu Liu"));
	set<studentInfo> studentSet(students.begin(), students.end());
	outputCont("student set", cout, studentSet.begin(), studentSet.end());
	//增 insert(a) 插入某个元素  set有序,所以怎么插入无所谓
	studentSet.insert(studentInfo("10000", "wjr"));
	outputCont("student set added", cout, studentSet.begin(), studentSet.end());
	//删 erase(it) 该函数用于根据元素的值或元素在集合中的迭代器位置来擦除它
	studentSet.erase(studentInfo("10010", "WuLiu"));
	outputCont("student set erased", cout, studentSet.begin(), studentSet.end());
	//改,set的迭代器it有const修饰符,那么对它元素的修改就必然不能成功了。
	//查 find用于检查元素是否属于集合,如果元素在集合容器中找到,则返回指向该元素的迭代器。否则返回set.end()
	//set<studentInfo>::iterator it = studentSet.find(studentInfo("10010", "WuLiu"));
	if (studentSet.find(studentInfo("10010", "WuLiu")) != studentSet.end())
	{
		cout << "find it!" << endl;
	}
	else
	{
		cout << "not found!" << endl;
	}
}

运行结果
在这里插入图片描述

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

在这里插入图片描述

map是STL的一个关联容器,它提供一对一的hash。
第一个可以称为关键字(key),每个关键字只能在map中出现一次;
第二个可能称为该关键字的值(value);

创建对象
map<T1,T2> m;

map<T1,T2, op> m; //op为排序规则,默认规则是less

void TestMap()
{
	map<char, int> s;   //用来存储字母出现次数的映射
	char c;				//存储输入字符
	do {
		cin >> c;//输入下一个字符
		if (isalpha(c))//判断是否是字母
		{
			c = tolower(c);//将字母转换为小写
			s[c]++;//将该字母的出现频率加1
		}
	} while (c != '.');//碰到“.”则结束输入
		//输出每个字母出现次数
	for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter)
		cout << iter->first << "" << iter->second << " ";
	cout << endl;
}

运行结果如下,输出结果为key+value,即出现的字母即次数。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值