首页 > 编程语言 > 详细

C++:函数模板和类模板

时间:2020-06-30 22:56:16      阅读:72      评论:0      收藏:0      [点我收藏+]

函数模板和类模板

所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属。

模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

函数模板

(1)为什么要有函数模板

需求:写 n 个函数,交换 char 类型、int 类型、double 类型变量的值。

void myswap_int(int &a, int &b)
{
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}

void myswap_char(char &a, char &b)
{
	char tmp;
	tmp = a;
	a = b;
	b = tmp;
}

// 函数模板
template <typename T>
void myswap(T &x, T &y)
{
	T tmp;
	tmp = x;
	x = y;
	y = tmp;
}

void main()
{
	// 正常编程
	{
		int a = 10;
		int b = 20;
		myswap_int(a, b);
		cout << "a: " << a << " b: " << b << endl;

		char x = ‘a‘;
		char y = ‘b‘;
		myswap_char(x, y);
		cout << "x: " << x << " y: " << y << endl;
	}
	// 使用函数模板
	{
		// 1. 显示类型调用
		int a = 30;
		int b = 40;
		myswap<int>(a, b);
		cout << "a: " << a << " b: " << b << endl;
		// 2. 自动类型推导
		char x = ‘c‘;
		char y = ‘d‘;
		myswap(x, y);
		cout << "x: " << x << " y: " << y << endl;
	}
	system("pause");
}

(2)函数模板做函数参数

实现不同类型的排序:

//  函数模板本质:类型参数化  
template <typename T1, typename T2>
void mySort(T1 *arr, T2 len)
{
	T1 tmp;
	T2 i, j;
	for (i = 0; i < len; i++)
	{
		for (j = i + 1; j < len; j++)
		{
			if (arr[i] > arr[j])
			{
				tmp = arr[i];
				arr[i] = arr[j];
				arr[j] = tmp;
			}		
		}
	}
}

template <typename T1, typename T2>
void myPrint(T1 *arr, T2 len)
{
	T2 i;
	for (i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void main()
{
	// int
	int myArr[] = { 10,20,30,40,50,60,33,66,44,32 };
	int Arrlen = sizeof(myArr) / sizeof(*myArr);
	mySort<int, int>(myArr, Arrlen);
	myPrint<int, int>(myArr, Arrlen);

	// char
	char myStr[] = "gdhientfw563790";
	int  Strlen = strlen(myStr);
	mySort<char, int>(myStr, Strlen);
	myPrint<char, int>(myStr, Strlen);
	system("pause");
}

(3)函数模板遇上函数重载

函数模板可以像普通函数一样被重载;

C++编译器优先考虑普通函数;

如果函数模板可以产生一个更好的匹配,那么选择模板;

可以通过空模板实参列表的语法限定编译器只通过模板匹配 ;

int Max(int a, int b) 
{ 
	cout << "int Max(int a, int b)" << endl;  
	return a > b ? a : b;
}

template<typename T> 
T Max(T a, T b) 
{ 
	cout << "T Max(T a, T b)" << endl;  
	return a > b ? a : b; 
}

template<typename T>
T Max(T a, T b, T c) 
{
	cout << "T Max(T a, T b, T c)" << endl; 
	return Max(Max(a, b), c);
}

void main()
{
	// 当函数模板和普通函数都符合调用时, 优先选择普通函数 
	cout << Max(1, 2) << endl;
	// 若显示使用函数模板, 则使用 <> 类型列表
	cout << Max<>(10, 20) << endl;
	// 如果函数模板产生更好的匹配 使用函数模板
	cout << Max(3.0, 4.0) << endl;
	// 重载
	cout << Max(5.0, 6.0, 7.0) << endl;
	// 调用普通函数
	cout << Max(‘a‘, 100) << endl;
}

(4)C++编译器函数模板机制

编译器并不是把函数模板处理成能够处理任意类的函数;

编译器从函数模板通过具体类型产生不同的函数;

编译器会对函数模板进行两次编译

在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。

类模板

类模板用于实现类所需数据的类型参数化;

类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响;

(1)单个类模板

类模板定义、使用、做函数参数

// 类模板定义
template <typename T>
class myClass
{
public:
	myClass(T a)
	{
		this->a = a;
	}
	void myPrint()
	{
		cout << "a: " << a << endl;
	}
private:
	T a;
};

// 类模板 做函数参数
void Print(myClass<int> &a)
{
	a.myPrint();
}
void Print(myClass<char> &a)
{
	a.myPrint();
}

void main()
{
	// 类模板使用
	myClass<int>	a1(10);
	myClass<char>	a2(‘c‘);

	Print(a1);
	Print(a2);
}

(2)类模板派生

类模板派生普通类;类模板派生类模板

结论: 子类从模板类继承的时候,需要让编译器知道父类的数据类型是什么(数据类型的本质:固定大小内存块的别名)

// 类模板定义
template <typename T>
class myClass
{
public:
	myClass(T t=0)
	{
		this->t = t;
	}
	void myPrint()
	{
		cout << "t: " << t << endl;
	}
protected:
	T t;
};

// 类模板派生普通类
class A : public myClass<int>
{
public:
	A(int a, int t) : myClass(t)
	{
		this->a = a;
	}
	void myPrint()
	{
		cout << "a: " << a << " t: " << t << endl;
	}
private:
	int a;
};

// 类模板派生模板类
template <typename T>
class B : public myClass<T>
{
public:
	B(T b, T t) : myClass<T>(t)
	{
		this->b = b;
	}
	void myPrint()
	{
		cout << "b: " << b << " t: " << t << endl;
	}
private:
	T b;
};

void main()
{
	// 类模板使用
	myClass<int> t(10);

	// 派生普通类
	A a(1, 2);
	a.myPrint();

	// 派生模板类
	B<double> b(10.1, 20.2);
	b.myPrint();
}

(3)类模板语法知识体系

  1. 所有的类模板函数写在类的内部
template <typename T>
class Complex
{
public:
	Complex(T a,T b){
		this->a = a;
		this->b = b;
	}
	void printCom(){
		cout << a << " + " << b << "i" << endl;
	}

	Complex operator+(Complex &a2){
		Complex tmp(a + a2.a, b + a2.b);
		return tmp;
	}

	friend ostream& operator<<(ostream &out, Complex &a){
		out << "a: " << a.a << " b:" << a.b;
		return out;
	}	
	friend Complex mySub(Complex &a1, Complex &a2){
		Complex tmp(a1.a - a2.a, a1.b - a2.b);
		return tmp;
	}
private:
	T a;
	T b;
};

void main()
{
	Complex<int> a1(1, 2);
	Complex<int> a2(3, 4);
	// 重载 + <<
	cout << a1 + a2 << endl;
	// 滥用友元函数(友元函数一般重载 << >> 操作符)
	{
		Complex<int> a3 = mySub(a1, a2);
		cout << a3 << endl;
	}
}
  1. 所有的类模板函数写在类的外部,在一个 cpp 中

结论:友元函数只用来进行 左移 右移 操作符重载。

友元函数:用友元函数重载 << >>

friend ostream& operator<< <T> (ostream &out, Complex<T> &c3) ;

// 类的前置声明 函数的前置声明      
template<typename T> 
class Complex;

template<typename T> 
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);

template <typename T>
class Complex
{
public:
	Complex(T a, T b);
	void printCom();

	Complex operator+(Complex &a2);

	friend ostream& operator<< <T> (ostream &out, Complex &a);

	// 滥用友元函数
	friend Complex<T> mySub <T> (Complex<T> &a1, Complex<T> &a2);

private:
	T a;
	T b;
};

// 构造函数实现 在类的外部
template <typename T>
Complex<T>::Complex(T a, T b){
	this->a = a;
	this->b = b;
}

// 成员函数实现 在类的外部
template <typename T>
void Complex<T>::printCom(){
	cout << a << " + " << b << "i" << endl;
}

// 重载实现 在类的外部
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &a2){
	Complex tmp(a + a2.a, b + a2.b);
	return tmp;
}

// 友元函数实现 在类的外部
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &a){
	out << "a: " << a.a << " b:" << a.b;
	return out;
}

// 不建议使用!
template <typename T>
Complex<T> mySub(Complex<T> &a1, Complex<T> &a2){
	Complex<T> tmp(a1.a - a2.a, a1.b - a2.b);
	return tmp;
}

void main()
{
	Complex<int> a1(1, 2);
	Complex<int> a2(3, 4);
	// 重载 + <<
	cout << a1 + a2 << endl;
	// 滥用友元函数(友元函数一般重载 << >> 操作符)
	{
		// (1)前置申明 (2)类的内部声明 (3)友元函数实现  (4)友元函数调用
		Complex<int> a3 = mySub<int>(a1, a2);
		cout << a3 << endl;
	}
}
  1. 所有的类模板函数写在类的外部,在不同的.h 和 .cpp中

要包含.cpp.hpp

complex.h

template <typename T>
class Complex
{
public:
	Complex(T a, T b);
	void printCom();
	Complex operator+(Complex &a2);
	friend ostream& operator<< <T> (ostream &out, Complex &a);

private:
	T a;
	T b;
};

complex.cpp

#include "complex.h"

// 构造函数实现 在类的外部
template <typename T>
Complex<T>::Complex(T a, T b){
	this->a = a;
	this->b = b;
}
// 成员函数实现 在类的外部
template <typename T>
void Complex<T>::printCom(){
	cout << a << " + " << b << "i" << endl;
}
// 重载实现 在类的外部
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &a2){
	Complex tmp(a + a2.a, b + a2.b);
	return tmp;
}
// 友元函数实现 在类的外部
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &a){
	out << "a: " << a.a << " b:" << a.b;
	return out;
}

test.cpp

#include "complex.cpp"
void main()
{
	Complex<int> a1(1, 2);
	Complex<int> a2(3, 4);
	// 重载 + <<
	cout << a1 + a2 << endl;
}

(4)类模板中的static关键字

从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个 static 数据成员;

和非模板类的 static 数据成员一样,模板类的 static 数据成员也应该在文件范围定义和初始化;

每个模板类有自己的类模板的 static 数据成员副本

// 类模板中的static关键字
template <typename T>
class myClass
{
public:
	static T m_a;
};
// 初始化static
template <typename T>
T myClass<T>::m_a = 0;

// m_a 应该是 每一种类型的类 使用自己的m_a
// -->  int  AA1::m_a  = 0;
// -->  char AA2::m_a  = 0;

void main()
{
	myClass<int> a1, a2, a3;
	a1.m_a = 1;
	a2.m_a++;
	a3.m_a++;
	cout << myClass<int>::m_a << endl;

	myClass<char> b1, b2, b3;
	b1.m_a = ‘a‘;
	b2.m_a++;
	b3.m_a++;
	cout << myClass<char>::m_a << endl;
}

编译器并不是把函数模板处理成能够处理任意类的函数;
编译器从函数模板通过具体类型产生不同的函数;
编译器会对函数模板进行两次编译
在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。

(5)类模板在开发中的应用

  1. 请设计一个数组模板类( MyVector ),完成对int、char、Teacher类型元素的管理。

--> stl 容器的概念

  1. 结论

结论1:如果把Teacher放入到MyVector数组中,并且Teacher类的属性含有指针,就是出现深拷贝和浅拷贝的问题。

结论2:需要Teacher封装的函数有: 1) 重写拷贝构造函数 2) 重载等号操作符 3) 重载左移操作符。

提高:所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以 STL 容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。

MyVector.h

template <typename T>
class MyVector
{
public:
	MyVector(int size = 0);
	MyVector(const MyVector &obj);
	~MyVector();
	
	friend ostream& operator<< <T> (ostream &out, MyVector &myV);
	T& operator[](int index);
    
    int getLen()
	{
		return m_len;
	}
private:
	T*	 m_space;
	int  m_len;
};

MyVector.cpp

#include "MyVector.h"

//MyVector<int> myV1(10);
template <typename T>
MyVector<T>::MyVector(int size = 0)
{
	m_space = new T[size];
	m_len = size;
}

//MyVector<int> myv2  = myv1;
template <typename T>
MyVector<T>::MyVector(const MyVector &obj)
{
	// 根据大小分配内存
	m_len = obj.m_len;
	m_space = new T[m_len];

	// copy data
	for (int i=0; i<m_len; i++)
	{
		m_space[i] = obj.m_space[i];
	}
}

template <typename T>
MyVector<T>::~MyVector()
{
	if (m_space != NULL)
	{
		delete[] m_space;
		m_space = NULL;
		m_len = 0;
	}
}

// 重载[]
template <typename T>
T& MyVector<T>::operator[](int index)
{
	return m_space[index];
}

// 重载<<
template <typename T>
ostream& operator<<(ostream &out, MyVector<T> &myV)
{
	for (int i=0; i<myV.getLen(); i++)
	{
		out << myV.m_space[i] << " ";
	}
	return out;
}

MyVector_test.cpp

#include "MyVector.cpp"

class Teachar
{
public:
	// 默认构造函数
	Teachar() {
		m_age = 18;
		m_pname = new char[1];
		strcpy(m_pname, "");
		//strcpy(m_name, "");
	}
	// 有参构造函数
	Teachar(char* name, int age) {
		m_age = age;
		m_pname = new char[strlen(name) + 1];
		strcpy(m_pname, name);
		//strcpy(m_name, name);
	}
	// 拷贝构造函数
	Teachar(const Teachar &obj) {
		m_age = obj.m_age;
		m_pname = new char[strlen(m_pname) + 1];
		strcpy(m_pname, obj.m_pname);
	}
	//析构函数 释放panme指向的内存空间
	~Teachar() {
		if (m_pname != NULL)
		{
			delete[] m_pname;
			m_pname = NULL;
			m_age = 18;
		}
	}

	void prinT() {
		cout << m_pname << " , " << m_age << endl;
		//cout << m_name << " , " << m_age << endl;
	}

	// 避免浅拷贝 重载 =
	Teachar& operator=(Teachar &obj)
	{
		// 1.清空内存
		delete[] m_pname;
		m_pname = NULL;
		m_age = 18;
		// 2.分配内存
		m_pname = new char[strlen(obj.m_pname) + 1];
		// 3.copy
		strcpy(m_pname, obj.m_pname);
		m_age = obj.m_age;
		return *this;
	}
	// 重载 <<
	friend ostream& operator<<(ostream &out, Teachar &obj)
	{
		out << obj.m_pname << " , " << obj.m_age << endl;
		return out;
	}

private:
	//char m_name[32];
	// 属性变成 char *panme, 构造函数里面 分配内存
	char*	m_pname;
	int		m_age;
};

void main()
{
	// 存储普通类型 int
	{
		// 定义变量
		MyVector<int> myV1(10);
		// 赋值,重载[]
		for (int i = 0; i < myV1.getLen(); i++)
		{
			myV1[i] = 1 + i;
			cout << myV1[i] << " ";
		}
		cout << endl;

		// 拷贝构造函数
		MyVector<int> myV2 = myV1;
		// 打印,重载<<
		cout << myV2 << endl;
	}

	// 存储普通类型 char
	{
		// 定义变量
		MyVector<char> myVc(26);
		// 赋值
		for (int i = 0; i < myVc.getLen(); i++)
		{
			myVc[i] = ‘a‘ + i;
		}
		cout << myVc << endl;
	}

	// 存储复杂类型 Teacher
	{
		Teachar t1("teacher1", 31), t2("teacher2", 32), t3("teacher3", 33), t4("teacher4", 34);
		
		// 存入 MyVector
		MyVector<Teachar> tArray(4);
		// 避免浅拷贝 重载 = 重写拷贝构造函数
		tArray[0] = t1;
		tArray[1] = t2;
		tArray[2] = t3;
		tArray[3] = t4;

		for (int i=0; i<4; i++)
		{
			tArray[i].prinT();
		}
		// Teacher 增加 <<
		cout << tArray;
	}

	// 存储复杂类型 Teacher*
	{
		Teachar t1("teacher1", 31), t2("teacher2", 32), t3("teacher3", 33), t4("teacher4", 34);

		MyVector<Teachar*> tArray(4);
		tArray[0] = &t1;
		tArray[1] = &t2;
		tArray[2] = &t3;
		tArray[3] = &t4;

		for (int i = 0; i < 4; i++)
		{
			Teachar *tmp = tArray[i];
			tmp->prinT();
		}
	}
}

C++:函数模板和类模板

原文:https://www.cnblogs.com/2dx3906/p/13216421.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!