函数模板&类模板&函数对象

345 阅读2分钟

模板&函数对象

经常使用C++编程应该都会遇到函数模板、类模板和函数对象吧,为了加深对模板的理解,写个帖子。

模板

函数模板

  • 语义形式:

template <模板参数表>

函数定义

  • 模板参数表的内容

类型参数:class(或typename) 标识符

常量参数:类型说明符 标识符

模板参数:template <参数表> class 标识符

#include <iostream>
using namespace std;

template <class T>	//定义函数模板
void outputArray(const T *array, int count) {
	for (int i = 0; i < count; i++)
		cout << array[i] << " ";
	cout << endl;
}	

int main() {	 //主函数
	const int A_COUNT = 8, B_COUNT = 8, C_COUNT = 20;
	int a [A_COUNT] = { 1, 2, 3, 4, 5, 6, 7, 8 };	//定义int数组
	double b[B_COUNT] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 }; //定义double数组
	char c[C_COUNT] = "Welcome to see you!";	//定义char数组

	cout << " a array contains:" << endl;
	outputArray(a, A_COUNT);	//调用函数模板
	cout << " b array contains:" << endl;
	outputArray(b, B_COUNT);	//调用函数模板
	cout << " c array contains:" << endl;
	outputArray(c, C_COUNT);	//调用函数模板

	return 0;
}

注意:

  • 一个函数模板并非自动可以处理所有类型的数据
  • 只有能够进行函数模板中运算的类型,可以作为类型实参
  • 自定义的类,需要重载模板中的运算符,才能作为类型实参

类模板

类模板的作用:

使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型和用户自定义类型)

类模板的声明
  • 语义形式:

template <模板参数表>

class 类名

{类成员声明};

如果需要在类模板以外定义其成员函数,则要采用形式

template<模板参数表>

类型名 类名<模板参数标识符列表>::函数名(参数表)

#include <iostream>
#include <cstdlib>
using namespace std;

struct Student {	// 结构体Student
	int id;		//学号
	float gpa;	//平均分
};  

template <class T>  //类模板:实现对任意类型数据进行存取
class Store {
private:
	T item;			// item用于存放任意类型的数据
	bool haveValue;	// haveValue标记item是否已被存入内容
public:
	Store();		// 缺省形式(无形参)的构造函数
	T &getElem();	//提取数据函数
	void putElem(const T &x);  //存入数据函数
};

//以下实现各成员函数。
template <class T>	//缺省构造函数的实现 
Store<T>::Store(): haveValue(false) { }

template <class T>                //提取数据函数的实现
T &Store<T>::getElem() {
	if (!haveValue) {	//如果试图提取未初始化的数据,则终止程序
		cout << "No item present!" << endl;
		exit(1);	//使程序完全退出,返回到操作系统。
		//参数可用来表示程序终止的原因,可以被操作系统接收
	}
	return item;    // 返回item中存放的数据 
}

template <class T>	//存入数据函数的实现 
void Store<T>::putElem(const T &x) {
	haveValue = true;	// 将haveValue 置为true,表示item中已存入数值
	item = x;			// 将x值存入item
}

int main() {
	Store<int> s1, s2;	//定义两个Store<int>类对象,其中数据成员item为int类型
	s1.putElem(3);	//向对象S1中存入数据(初始化对象S1)
	s2.putElem(-7);	//向对象S2中存入数据(初始化对象S2)
	cout << s1.getElem() << "  " << s2.getElem() << endl;	//输出对象S1和S2的数据成员

	Student g = { 1000, 23 };	//定义Student类型结构体变量的同时赋以初值
	Store<Student> s3;	//定义Store<Student>类对象s3,其中数据成员item为Student类型
	s3.putElem(g); //向对象D中存入数据(初始化对象D)
	cout << "The student id is " << s3.getElem().id << endl;	//输出对象s3的数据成员

	Store<double> d;	//定义Store<double>类对象s4,其中数据成员item为double类型
	cout << "Retrieving object d... ";
	cout << d.getElem() << endl;  //输出对象D的数据成员
	//由于d未经初始化,在执行函数D.getElement()过程中导致程序终止

	return 0;
}

多文件结构中模板的组织

模板的实例化机制

对一个类模板进行实例化时,只有它的成员的声明会被实例化。而对类模板成员函数定义的实例化也是按需进行的,只有一个函数会被使用时,它才会被实例化。类的静态数据成员的定义的实例化,同样是按需进行的。

以上机制决定了函数模板、类模板成员函数和类模板静态数据成员不能想普通函数、普通类的成员函数和普通类的静态数据成员那样把定义放到源文件中,而只把声明放在头文件。

以上内容参考《C++程序语言设计》

函数对象

  • 一个行为类似函数的对象

  • 可以没有参数,也可以带有若干参数

  • 其功能是获取一个值,或者改变操作的状态

    • 普通函数就是函数对象

    • 重载了“()”运算符的类的实例是函数对象

函数对象概念图

#include <iostream>   
#include <numeric>	//包含数值算法头文件
using namespace std;

int mult(int x, int y) { return x * y; };	//定义一个普通函数
int main() {
	int a[] = { 1, 2, 3, 4, 5 };
	const int N = sizeof(a) / sizeof(int);
	cout << "The result by multipling all elements in a is "
		<< accumulate(a, a + N, 1, mult)	//将普通函数mult传递给通用算法
		<< endl;
	return 0;
}
#include <iostream>
#include <numeric>	//包含数值算法头文件
using namespace std;

class MultClass	{  //定义MultClass类
public:
	int operator() (int x, int y) const { return x * y; }	//重载操作符operator()
};

int main() {
	int a[] = { 1, 2, 3, 4, 5 };
	const int N = sizeof(a) / sizeof(int);
	cout << "The result by multipling all elements in a is "
		<< accumulate(a, a + N, 1, MultClass())	//将类multclass传递给通用算法
		<< endl;
	return 0;
}