C++模板与泛型

(一) 模板与泛型

⑴ 泛型

       它是一种泛化的编程方式,其实现原理为程序员编写一个函数/类的代码示例,让编译器去填补出不同的函数实现。允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换言之,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

⑵ 模板

       模板是泛型(泛型generic type——通用类型之意)编程的基础,是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector 或 vector 。

模板是一种对类型进行参数化的工具,可以是如下形式:

  • 函数模板:

  • 类模板:

  • 别名模板(C++11):

  • 变量模板(C++14):

  • 约束与概念(C++20):

模板是一种忽略数据一种泛型编程。把数据当做未知量,当使用的传入类型一种编程方式。

⑶  为什么要有模板的原因?

现实中,我们可能先用设计一个包含两个int参数的函数,用来求两个对象的和,在实践中我们发现我们可以还需要其他类型的求两个对象之和,则需要定义n多个函数

int Max(int a, int b) 
{
    return a > b ? a : b;
}
string Max(string a, string b) 
{
    return a > b ? a : b;
}
double Max(double a, double b) 
{
    return a > b ? a : b;
}

写这样相似代码,有点麻烦,冗余,故用一个模板来总结,该比较怎样的数据,就传对应的参数给模板。

       这个例子就是实现了多种不同类型的两个数之间的交换,我们这里只是实现了三种,如果要实现很多种,那代码量就非常大。如果可以只用一个函数和类来描述,那将会大大 减少代码量,并能实现程序代码的复用性,所以这里就要用到模板了。

     对于函数功能相同,唯一的区别就是参数类型不同,在这种情况下,不必定义多个函数,只需要在模板中定义一次即可。在调用函数时编译器会根据实参的类型生成对应类的函数实体(调用的不是模板,而是模板实例化出来的实体),从而实现不同的函数功能。

PS:编译器根据实参生成特定的函数的过程叫做模板实例化

语法格式:

template <class T>   //告诉编译器,接下来要用到一个未知类型是T类型
//template <typename T>   typename等效用class
template <class T1,class T2,class T3>    //三个未知类型

具体代码:

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

template <class Type> Type Max(Type a, Type b)
{
	return a > b ? a : b;
}
//两种写法没有区别
template<class Type>
void print(Type a)
{
	cout << a << endl;
}
int main()
{
	 print(Max(2, 6));
	 print(Max(3.2, 0.6));
	 print(Max("3.2", "0.6"));
	return 0;
}

(二)C++函数模板

  •   函数模板,就是定义一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。实际上是定义一个可以生成任意类型函数的模板,它所用到的数据的类型均被作为参数:不指定具体类型,而是用一个虚拟 的类型来代替(实际上是用一个标识符来占位)。凡是函数体相同的函数都可以用这个模板来代替,在函数调用时根据传入的实参来逆推出真正的类型,从而产生一个针对该类型的实体函数。这个通用函数就称为函数模板。

  • 所有函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。

    函数模板声明语法:

    template <模板参数表>

    类型名 函数名  (参数表){
    函数体的定义

    }

    template<typename Type,...> //注意后面不能加分号
    Type funName(Type val){
        //Code
    }

    template: 是声明模板的关键字,告诉编译器开始泛型编程。

    <typename type>:尖括号中的typename是定义模板形参的关键字,用来说明后面的模板形参是类型。typename还可以用class关键字来代替,但是推荐用typename 。

  • 函数模板调用

    • 函数模板隐式调用

    • 显式调用 : 函数名<未知类型>(函数参数)

    • 自动类型推导:根据参数的类型进行推导,但是两个参数的类型必须一致,否则会报错。

  • 函数模板本质就是函数传参

    • 函数模板也是可以缺省

  • 函数模板种存在变量

    • 这种函数模板必须显示调用

    • 变量传参只能传入常量

  • 当函数模板和普通函数相遇

    • 优先调用类型一致的普通函数

    • 显式调用一定是调用模板

  • 函数模板重载

    • 优先调用传参数目少的函数模板

  •  具体代码:

#include <iostream>
#include <string>
using namespace std;
template <class Type>
Type  Max(Type a, Type b)
{
	return a > b ? a : b;
}
template <class _Ty1, class _Ty2, class _Ty3>
void print(_Ty1 one, _Ty2 two, _Ty3 three)
{
	cout << "1:" << one << endl;
	cout << "2:" << two << endl;
	cout << "3:" << three << endl;
}
//函数模板的缺省
template <class _Ty1, class _Ty2 = string, class _Ty3 = int>
void printData(_Ty1 one, _Ty2 two, _Ty3 three)
{
	cout << "1:" << one << endl;
	cout << "2:" << two << endl;
	cout << "3:" << three << endl;
}

template <class _Ty, int size>
_Ty* createArray()
{
	_Ty* parray = new _Ty[size]; 
	return parray;
}
template <class _Ty, int size>
void printArray(_Ty array[])
{
	for (int i = 0; i < size; i++)
	{
		cout << array[i] << "\t";
	}
	cout << endl;
}


template <class _Ty, int size = 3>
void printArray2(_Ty array[])
{
	for (int i = 0; i < size; i++)
	{
		cout << array[i] << "\t";
	}
	cout << endl;
}

void test()
{
	//No.4 函数模板存在变量
	int* pInt = createArray<int, 5>();			//_Ty =int  size=5
	string* pStr = createArray<string, 5>();
	double* pDouble = createArray<double, 5>();
	//必须要显式调用
	int array[3] = { 1,2,3 };
	printArray<int, 3>(array);
	double dNum[3] = { 1.11,2.33,4.44 };
	printArray<double, 3>(dNum);
	//变量做了缺省,可以隐式调用
	printArray2(array);
	printArray2(dNum);
	
}

void Func1(int a, string b, double c)
{
	cout << "普通函数" << endl;
}
//函数模板允许重载
template <class _Ty1, class _Ty2, class _Ty3>
void Func1(_Ty1 a, _Ty2 b, _Ty3 c)
{
	cout << "三个类型模板" << endl;
}
template <class _Ty1, class _Ty2>
void Func1(_Ty1 a, _Ty2 b, _Ty2 c)
{
	cout << "两个类型模板" << endl;
}
template <class _Ty1>
void Func1(_Ty1 a, _Ty1 b, _Ty1 c)
{
	cout << "一个类型模板" << endl;
}

void test2()
{
	Func1<int, string, double>(1, "sd", 1.11);
	Func1(1, string("sd"), 1.11);
	Func1(1, 1, 1);			//一个参数的模板
	Func1("string", 1, 1);
	Func1("string", 1, "sfsd");
}
int main()
{
	//No.1 隐式调用法
	cout << Max(string("abc"), string("dbc")) << endl;  //Type=string
	cout << Max(1, 2) << endl;							//Type=int
	cout << Max(1.11, 2.33) << endl;					//Type=double
	//No.2 显示调用
	cout << Max<string>(string("abc"), string("dbc")) << endl;  //Type=string
	cout << Max<int>(1, 2) << endl;							//Type=int
	cout << Max<double>(1.11, 2.33) << endl;					//Type=double
	print<int, string, double>(1, "ILoveyou", 1.11);
	print<string, string, string>("abc", "ILoveyou", "模板");

	//No.3 缺省
	printData<string>("str1", "str2", 1);		//_Ty1=string _Ty2=string _Ty3=int
	printData<string, double>("str1", 1.11, 1);
	printData<string, double, string>("str1", 1.11, "str3");
	test();
	test2();


	return 0;
}

(三)类模板

       使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取不同类型(如系统预定义和用户自定义的)。

       类模板则是对不同类的公共性质的抽象,而类是对一组对象的公共性质的抽象,因此类依是属于更高层次的抽象。由于类模板需要一种或多种类型参数,所以类模板也常常称参数化类。当然,模板参数的实参也不总是可以用任何类型的。

类模板声明的语法形式是:
template<模板参数表>
class 类名{

类成员声明

};


  其中类成员声明的方法与普通类的定义几乎相同,只是在它的各个成员(数据成员和函敬成员)中通常要用到模板的类型参数T。其中“模板参数表”的形式与函数模板中的“模板数表”完全一样。
如果需要在类模板以外定义其成员函数,则要采用以下的形式:
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
      一个类模板声明自身并不是一个类,只有当被其他代码引用时,模板才根据引用的需要
生成具体的类。
使用一个模板类来建立对象时,应按如下形式声明:
模板名<模板参数表>对象名1,…,对象名 n;

  具体代码:

#include <iostream>
#include <string>
using namespace std;
template <class _Ty1, class _Ty2>
class Data
{
public:
	void print();
protected:
public:
	static int count;
};

template <class _Ty1, class _Ty2>
int Data<_Ty1, _Ty2>::count = 0;

template <class _Ty1, class _Ty2>
void Data<_Ty1, _Ty2>::print()
{
	cout << "类模板中函数" << endl;
}

template <class _Ty1, class _Ty2>
class Son :public Data<_Ty1, _Ty2>
{
public:

protected:

};

struct MMInfo
{
	string name;
	int age;
	int num;
};
ostream& operator<<(ostream& out, const MMInfo& object)
{
	out << object.name << "\t" << object.age << "\t" << object.num;
	return out;
}


struct Score
{
	int math;
	int english;
	int py;
};

ostream& operator<<(ostream& out, const Score& object)
{
	out << object.math << "\t" << object.english << "\t" << object.py;
	return out;
}

template <class _Ty1, class _Ty2>
class MM
{
public:
	MM(_Ty1 one, _Ty2 two) :one(one), two(two)
	{

	}
	void print()
	{
		cout << one << "\t" << two << endl;
	}
protected:
	_Ty1  one;
	_Ty2  two;
};
void testMM()
{
	MM<string, int> mm("月亮", 18);
	mm.print();
	MM<int, int> complex(1, 2);
	complex.print();
	MMInfo  info = { "美女",18,1001 };
	Score score = { 88, 99, 100 };
	MM<MMInfo, Score> mmObject(info, score);
	mmObject.print();
}
//类模板特化

template <class _Ty1, class _Ty2, class _Ty3>
class A
{
public:
	A(_Ty1 one, _Ty2 two, _Ty3 three) :one(one), two(two), three()
	{
		cout << "三个类型" << endl;
	}
protected:
	_Ty1 one;
	_Ty2 two;
	_Ty3 three;
};

//局部特化
template <class _Ty1>
class A<_Ty1, _Ty1, _Ty1>
{
public:
	A(_Ty1 one, _Ty1 two, _Ty1 three) :one(one), two(two), three()
	{
		cout << "局部特化" << endl;
	}
protected:
	_Ty1 one;
	_Ty1 two;
	_Ty1 three;
};
 
 
void testA()
{
	A<int, int, int>  object1(1, 1, 1);
	A<double, double, double>  object2(1.1, 1.1, 1.1);
	A<double, string, double>  object3(1.1, string("模板"), 1.1);
}


int main()
{
	Data<int, string>  object;
	Data<int, string>* pObject = new Data<int, string>;
	object.print();
	pObject->print();

	cout << Data<int, int>::count << endl;
	cout << Data<string, int>::count << endl;
	testMM();
	testA();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘 关

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值