函数模板与类模板

函数模板:

所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体

指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数

体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中

定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类

型,从而实现了不同函数的功能


模板:将 算法 与 数据类型 相分离,专注于算法的实现


一、函数模板定义

// 函数模板定义:用 template 将函数声明成函数模板
template <typename T> 
void mySwap(T &a,  T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;

	mySwap(a, b);    // 可以隐式调用 函数模板 编译器会自己判断变量的类型
	// cout << "a = " << a << ", b = " << b << endl;
	myPrint(a, b);

	// 变量类型进行参数化:如何传类型?
	mySwap<int>(a, b);
	// cout << "a = " << a << ", b = " << b << endl;
	myPrint<int>(a, b);

	char c1 = 'a';
	char c2 = 'b';
	mySwap<char>(c1, c2);
	// cout << "a = " << c1 << ", b = " << c2 << endl;
	myPrint<char>(c1, c2);
	
	double d1 = 1.2;
	double d2 = 2.3;
	mySwap<double>(d1, d2);
	// cout << "a = " << d1 << ", b = " << d2 << endl;
	myPrint<double>(d1, d2);
    return 0;
}


上面程序中的mySwap()被声明成函数模板, T 相当于虚拟类型  传类型的时候用 <参数类型> 就行了


二、函数模板的性质

1、函数模板调用和普通函数的区别:

普通函数支持类型的隐式转换,函数模板不支持类型的隐式转换

如果 普通函数在被调用时 如果实参类型 与 形参类型不一致, 会自动转换


2、函数模板和普通函数可以共存

1、当函数模板和普通函数都可以调用的时候,优先调用普通函数

2、如果想调用函数模板,可以用空参数列表 <> 进行说明

3、当函数模板可以提供更优选择的时候,优先调用函数模板

4、函数模板也是可以重载的

template <typename T>
int myAdd(T a, T b)
{
	return a+b;
}

template <typename T>
int myAdd(T a, T b, T c)
{
	return a+b+c;
}


三、模板机制

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

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


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

第一次:在声明的地方对模块代码本身进行编译

第二次:在调用的地方对参数替换后的代码进行替换

说明:通过 函数模板 产生的函数 被称为 模板函数


四、类模板

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

类模板在表示如数组、表、图等数据结构显得特别重要,这些数据结构的表示

和算法不受所包含的元素类型的影响

1、声明一个类模板

template <typename T>
class A
{
public:
	A(T a)
	{
		this->a = a;
	}

	void print ()
	{
		cout << "a = " << a << endl;
	}
private:
	T a;
};

2、类模板对象做函数参数传递的时候, 要写一个 具体的 模板类对象

void myPrint(A<int>  &a)
{
	a.print();
}

void myPrint(A<double> &a)
{
	a.print();
}

3、使用函数模板

template <typename T>
void  printA(A<T> &a)
{
	a.print();
}

4、函数模板可以隐式调用,但类模板不可以隐式调用,必须说明模板类型

A<int> a(10);   
	A<double> d(1.2);

	myPrint(a);
	myPrint(d);

	printA(a);
	printA(d);

5、派生一个具体的类,继承自一个具体的模板类

class B : public A<int>
{
public:
	B(int a,  int b): A(a)
	{

	}
private:
	int b;
};

6、派生一个类模板

template <typename T>
// class C:public A<T>
class C: public A<int>
{
public:
	C(int a, T c):A(a)
	{

	}
private:
	T c;
};

五、类的实现

1、类的实现放在类的内部

#include <iostream>

using namespace std;

template <typename T>
class Complex
{

	// 友元函数写到类的内部
	// 虽然写到了类的内部,但是该函数还是友元函数,不是类的内部函数
	friend ostream &operator << (ostream &out, Complex &c)
	{
		out << c.a << " + " << c.b << "i";
		return out;
	}

	friend Complex mySub(Complex &c1, Complex &c2)
	{
		Complex tmp(c1.a - c2.a, c1.b - c2.b);
		return tmp;
	}
public:
	Complex(T a = 0, T b = 0)
	{
		this->a = a;
		this->b = b;
	}

	Complex operator +(Complex &c)
	{
		Complex tmp(a + c.a, b + c.b);
		return tmp;
	}
private:
	T a;
	T b;
};

int main()
{
	Complex<int> c1(1, 2), c2(3, 4), c;


	// mySub的实现 虽然是写到了类的内部,但是它还是一个友元函数
	// 如果是类的内部函数 调用如下:  c1.mySub(c2)
	// 但因为 mySub  是友元函数,实际是一个外部的全局函数,所以只能用如下调用:mySub(c1, c2);
	c = mySub(c1, c2);

	cout << c1 << endl;
	cout << c << endl;

	c = c1 + c2;
	cout << c << endl;

    return 0;
}


2、类的实现放在类的外部(同一文件中)

类的成员函数在类的外部实现时,需要将成员函数写成函数模板

#include <iostream>

using namespace std;

// 声明类
template <typename T>
class Complex;

// 函数声明
template <typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2);

template <typename T>
class Complex
{
	friend ostream &operator<< <T>(ostream &out, Complex<T> &c);
	
	friend Complex<T> mySub <T>(Complex<T> &c1, Complex<T> &c2);
public:
	Complex(T a = 0, T b = 0);
	Complex operator +(Complex &c);

private:
	T a;
	T b;
};

template <typename T>
Complex<T>::Complex(T a = 0, T b = 0)
{
	this->a = a;
	this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator +(Complex &c)
{
	Complex tmp(a + c.a, b + c.b);
	return tmp;
}

template <typename T>
ostream & operator << (ostream &out, Complex<T> &c)
{
	out << c.a << " + " << c.b << "i";
	return out;
}


template <typename T>
Complex<T> mySub(Complex<T> &c1, Complex<T> &c2)
{
	Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
	return tmp;
}

int main()
{
	Complex<int> c1(1, 2), c2(3, 4), c;
	c = mySub(c1, c2);

	cout << c1 << endl;
	cout << c << endl;

	c = c1 + c2;
	cout << c << endl;

	return 0;
}

3、类的实现放在类的外部(不同文件中)

只要将函数实现" cpp "文件,改名为 " hpp "作为 main.cpp


template <typename T>

Complex<T>::Complex(T a = 0, T b = 0); //外部原型

//前面的 Complex<T> 说明是 Complex 的成员函数

Complex (T a = 0, T b = 0); //类中声明


template<typename T>

Complex<T> Complex<T>::operator+(Complex &c);//外部原型

第一个是 Complex<T> 是返回值

第二个是 Complex<T> 说明是 Complex 的成员函数

Complex operator+(Complex &c); //类中声明


template <typename T>

ostream operator << (ostream &out, Complex<T> &obj); //类外部原型

friend ostream operator << <T>(ostream &out, Complex<T> &obj);//类中声明


template <typename T>

Complex<T> muSub(Complex<T> &c1, Complex<T> &c2);//外部原型

friend Complex<T> mySub<T>(Complex<T> &c1, Complex<T> &c2);//类中声明


六、类模板中的 static 关键字

同一类型 的 模板类的对象 共享 static

不属于同一类型的 模板类的对象 不共享 static

template <typename T>
T A<T>::sa = 1; 

class A_int
{
public:
	int a;
	static int sa;
};
int A_int::sa = 1; 


class A_double
{
public:
	double a;
	static double sa;
};
double A_double::sa = 1; 


int main()
{
	A<int>  a;
	a.sa = 10;

	A<double> d;
	cout << d.sa << endl;        //sa = 1

	cout << A<int>::sa << endl;  //sa = 10
	return 0;
}




猜你喜欢

转载自blog.csdn.net/ljf_djcrs/article/details/79370097