C++核心编程Day06

1.模板

1.1 模板概论
	c++提供了函数模板,即是建立一个通用函数,其函数类型和形参类型不具体制定,用
一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以
用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统
会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。
	c++提供两种模板机制:函数模板和类模板

	总结:
	1.模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属。
	2.模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。

1.2 函数模板
	函数模板的形式如下所示:
	template<class T>
	void swapT(T& a, T& b);
	其中template<class T>,也可写为template<typename T>
			
	注:1.编译器会根据实参自动推导数据类型
		2.显示的知道数据类型(指定了类型不能传入非指定类型的数据,必须要让编译器
		知道你要传入的数据类型)
		3.隐式转换:如果参数列表指定数据类型,那么实参中可以隐式转换
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template<class T>
void swapT(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
template<class T>
void swapT2()
{
}

void test01()
{
	int a = 10;
	int b = 20;

	//1.编译器会根据实参自动推导数据类型
	swapT(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	//2.显示的知道数据类型
	swapT<int>(a, b);
	//swapT<double>(a, b);//指定了类型不能传入非指定类型的数据
	//swapT2<>();//error,必须要让编译器知道你要传入的数据类型
}

template <class T>
int func(T a, T b)
{
	return a + b;
}

//另外一种写法,于上述写法作用是相同的
template <typename T>
int func1(T a, T b)
{
	return a + b;
}

void test02()
{
	int a = 10;
	double b = 20.2;
	//隐式转换:如果参数列表指定数据类型,那么实参中可以隐式转换
	cout << func<int>(a, b) << endl;
}


int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}
1.3 案例:函数模板实现数组排序
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template<class T>
void swapArr(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

template<class T>
void sortArr(T arr[],int len)
{
	for (int i = 0; i < len-1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				swapArr<T>(arr[j], arr[j + 1]);
			}
		}
	}
}

template <class T>
void printArr(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << endl;
	}
}

void test01()
{
	int arr[10] = { 8,6,9,2,1,4,5,3,7,0 };
	int len = sizeof(arr) / sizeof(arr[0]);
	sortArr<int>(arr, len);
	printArr<int>(arr, len);
}

int main()
{
	test01();
	system("pause");
	return 0;
}
1.4 函数模板和普通函数的区别
	1.函数模板不允许自动类型转化
	2.普通函数能够自动进行类型转化
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

int myAdd(int a, int b)
{
	return a + b;
}

template <class T>
int myAdd1(T a, T b)
{
	return a + b;
}

void test()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	//普通函数可以进行隐式的类型转换
	myAdd(a, c);
	//函数模板不可以进行隐式的类型转换,如果要进行隐式转换要加上参数列表
	//myAdd1(a, c);
	myAdd1<int>(a, c);
}

int main()
{
	test();
	system("pause");
	return 0;
}
1.5 函数模板和普通函数调用规则
	1.c++编译器优先考虑普通函数
	2.可以通过空模板实参列表的语法限定编译器只能通过模板匹配
	3.函数模板可以像普通函数那样可以被重载
	4.如果函数模板可以产生一个更好的匹配,那么选择模板
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

void myAdd(int a, int b)
{
	cout << "普通函数的调用!" << endl;
}

//1.函数模板和普通函数之间可以进行重载
template <class T>
void myAdd(T a, T b)
{
	cout << "函数模板1的调用!" << endl;
}

template <class T>
void myAdd(T a, T b,T c)
{
	cout << "函数模板2的调用!" << endl;
}

void test()
{
	int a = 10;
	int b = 20;
	//2.二者默认重载的时候,会优先调用普通函数
	myAdd(10, 20);
	//3.可以使用空参数列表强制调用函数模板
	myAdd<>(10, 20);
	//4.函数模板之间也可以进行重载
	myAdd(10, 20, 3);
	//5.如果函数模板可以产生更好的匹配,优先使用函数模板
	char a1 = 'a';
	char b1 = 'b';
	myAdd(a1, b1);
}

int main()
{
	test();
	system("pause");
	return 0;
}
1.6 模板实现机制
函数模板机制总结:
1.编译器并不是把函数模板处理成能够处理任何类型的函数
2.函数模板通过具体类型产生不同的函数
3.编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
4.函数模板具有局限性,例如对于一个自定义数据类型进行排序时,模板就不能起
到预期的作用。(解决办法:模板具体化,不建议使用)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class A
{
public:
	A()
	{
		this->name = "张三";
		this->age = 0;
	}
public:
	string name;
	int age;
};

//假如传入的是自定义数据类型的话,就不能直接比较,那么就需要模板具体化
template <class T>
void comPare(T& a, T& b)
{
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

//模板具体化的前提是要先有这个模板才可以进行具体化
//不建议具体化函数模板,因为不具有模板的通用性
template<> void comPare<A>(A& a, A& b)
{
	cout << "函数模板的具体化" << endl;
	if (a.age > b.age)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

void test()
{
	A a1;
	A a2;
	comPare(a1, a2);
}

int main()
{
	test();
	system("pause");
	return 0;
}
1.7 类模板基本概念
	类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个
类,其功能是相同的,仅仅是数据类型不同。
	类模板用于实现类所需数据的类型参数化
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template <class Nametype,class Agetype>
class A
{
public:
	A(Nametype name, Agetype age)
	{
		this->name = name;
		this->age = age;
	}

	void myPrint()
	{
		cout << this->name << " " << this->age << endl;
	}

public:
	Nametype name;
	Agetype age;
};

void test()
{
	//实例化对象,使用类模板必须要指定参数类型
	//类模板不会自动推导函数参数类型,函数模板可以自动推导参数类型
	A<string, int> a("张三", 18);
	a.myPrint();
	A<int, int> b(20, 18);
	b.myPrint();
}

//类模板也可以有默认参数,不过于函数中不同的是,类模板中的参数类型是数据类型
//template <class Nametype, class Agetype = int,class T>//错误的,假如有一个有默认参数,则之后的参数也应该到要有默认参数

template <class Nametype, class Agetype = int>
class B
{
public:
	B(Nametype name, Agetype age)
	{
		this->name = name;
		this->age = age;
	}

	void myPrint()
	{
		cout << this->name << " " << this->age << endl;
	}

public:
	Nametype name;
	Agetype age;
};

void test2()
{
	B<string> b("张三", 18);
	b.myPrint();
	//也可以手动传入数据类型
	B<string,double> b1("张三", 18.12);
	b1.myPrint();
}

int main()
{
	//test();
	test2();

	system("pause");
	return 0;
}
1.8 案例:复数的模板类及重载
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template <class Idtype, class Agetype>
class A
{
public:
	A(Idtype id, Agetype age)
	{
		this->id = id;
		this->age = age;
	}
	//成员函数
	//A& myAdd(A& a)
	//{
	//	this->id = this->id + a.id;
	//	this->age = this->age + a.age;
	//	return *this;
	//}
	//运算符重载
	A& operator+(A& a)
	{
		this->id = this->id + a.id;
		this->age = this->age + a.age;
		return *this;
	}

	void myPrint()
	{
		cout << this->id << " " << this->age << endl;
	}

public:
	Idtype id;
	Agetype age;
};

void test()
{

	A<int, int> a(3, 4);
	//a.myPrint();
	A<int, int> b(5, -10);
	//b.myPrint();
	//成员函数
	//A<int, int> c = a.myAdd(b);
	//运算符重载
	A<int, int> c = a + b;
	c.myPrint();
	//c.myPrint();
}


int main()
{
	test();

	system("pause");
	return 0;
}
1.9 类模板做函数参数
有以下三种类型:
	1.指定传入的数据类型
	2.把参数模板化
	3.把整个类模板化
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template <class Nametype, class Agetype>
class A
{
public:
	A(Nametype name, Agetype age)
	{
		this->name = name;
		this->age = age;
	}
	void myPrint()
	{
		cout << this->name << " " << this->age << endl;
	}

public:
	Nametype name;
	Agetype age;
};

//1.指定传入的数据类型
void func1(A<string,int> &a)
{
	a.myPrint();
}
//2.把参数模板化
template <class T1,class T2>
void func2(A<T1, T2>& a)
{
	a.myPrint();
}
//3.把整个类模板化
template <class T1>
void func3(T1& a)
{
	a.myPrint();
}
void test()
{
	A<string, int> a("张三", 4);
	func1(a);

	func2(a);

	func3(a);
}


int main()
{
	test();

	system("pause");
	return 0;
}
1.10 类模板的继承
	1.普通类继承类模板(注:不能使用普通的继承写法,继承类模板的时候要告诉编译
	器父类的泛型数据是什么类型)
	2.类模板继承类模板(注:必须在实例化的时候告诉父类是什么数据类型)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//普通类继承类模板
template<class T>
class Father
{
public:
	Father()
	{
		m = 20;
	}
public:
	T m;
};
//普通类
class Son :public Father<int>//不能使用普通的继承写法,继承类模板的时候要告诉编译器父类的泛型数据是什么类型
{
public:

};

//类模板继承类模板
//必须在实例化的时候告诉父类是什么数据类型
template<class T1,class T2>
class Son2:public Father<T2>
{

};

void test01()
{
	Son2<int, int> a;
	cout << a.m << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
1.11 类模板成员类外实现
	类模板的成员函数在类外实现时,要写出函数模板
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

template<class Nametype,class Agetype>
class A
{
public:
	A(Nametype name, Agetype age);

	void myPrint();

public:
	Nametype name;
	Agetype age;
};
//类模板的成员函数在类外实现
//要写出函数模板
template<class Nametype, class Agetype>
A<Nametype, Agetype>::A(Nametype name, Agetype age)
{
	cout << "构造函数" << endl;
	this->age = age;
	this->name = name;
}
template<class Nametype, class Agetype>
void A<Nametype, Agetype>::myPrint()
{
	cout << this->name << " " << this->age << endl;
}


void test01()
{
	A<string,int> a("张三",11);
	a.myPrint();
}

int main()
{
	test01();
	system("pause");
	return 0;
}
1.12 类模板的分文件编写

	1.调用类模板的时候,要进行二次编译,要把泛型的数据类型转化为具体的类型,这
时需要知道函数体,但是函数的实现在.cpp中,那么调用类模板的.CPP没有引入实现
的.cpp,只引入了.h文件,因此会报错

	2.解决方法,将类模板的声明于实现放在一个.hpp文件中,这样函数在二次编译的时
候,仅引入一个文件就可以知道函数的实现了

	3.为什么.hpp有类成员的实现,在调用类模板的地方引入.hpp,不会报重定义?类的成
员函数默认申请为内联函数,在链接的时候,链接器会对重名的成员函数进行处理,只
保留一份成员函数,所以不会报错

1.13 类模板和友元
在类模板中,友元的实现有两种方式:
	1.友元在类模板类内实现
	2.友元在类模板类外实现
		2.1.在函数名和括号中间加<>表示空参数列表,以此来调用函数模板
		2.2.编译器不知道下面有没有这个友元函数模板的实现,需要知道函数的结构
		2.3 告诉编译器下面有类外友元函数的实现
		2.4 类外友元函数声明需要声明友元所在的类
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//告诉编译器下面有类外友元函数的实现
//类外友元函数声明需要声明类A
template<class Nametype, class Agetype>
class A;
//类外友元函数声明
template<class Nametype, class Agetype>
void myprintA(A<Nametype, Agetype>& a);

template<class Nametype, class Agetype>
class A
{
	//1.友元函数可以类内实现
	friend void printA(A<Nametype, Agetype>& a)
	{
		cout << "类内实现" << a.name << " " << a.age << endl;
	}
	//2.友元函数类外实现
	//在函数名和括号中间加<>表示空参数列表,以此来调用函数模板
	//编译器不知道下面有没有这个友元函数模板的实现,需要知道函数的结构
	friend void myprintA<>(A<Nametype, Agetype>& a);

public:
	A(Nametype name, Agetype age)
	{
		this->name = name;
		this->age = age;
	}

	void myPrint()
	{
		cout << "打印成员函数" << endl;
	}

private:
	Nametype name;
	Agetype age;
};

void test01()
{
	A<string, int> a("张三", 2);
	printA(a);
}

//2.友元函数类外实现

template<class Nametype, class Agetype>
void myprintA(A<Nametype, Agetype>& a)
{
	cout << "类外实现" << a.name << " " << a.age << endl;
}

void test02()
{
	A<string, int> a("张三", 2);
	myprintA(a);
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}
1.14 案例:类模板实现数组
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

//数组模板
template <class T>
class MyArr
{
public:
	MyArr()
	{

	}
	MyArr(int capacity)
	{
		this->capacity = capacity;
		this->size = 0;
		this->p = new T[this->capacity];
		memset(this->p, 0, this->capacity);
	}
	MyArr(const MyArr& a)
	{
		this->capacity = a.capacity;
		this->size = a.size;
		this->p = new T[this->capacity];
		memset(this->p, 0, this->capacity);


		//memcpy(this->p, a.p);
		for (int i = 0; i < this->size; i++)
		{
			this->p[i] = a.p[i];
		}
	}

	//赋值=重载函数
	MyArr& operator=(const MyArr& a)
	{
		if (this->p != NULL)
		{
			delete[] p;
			p = NULL;
		}
		this->capacity = a.capacity;
		this->size = a.size;
		this->p = new T[this->capacity];
		memset(this->p, 0, this->capacity);

		for (int i = 0; i < this->size; i++)
		{
			this->p[i] = a.p[i];
		}
		
		return *this;
	}
	//重载[]函数
	T& operator[](int index)
	{
		return this->p[index];
	}
	//尾插
	void pushmyBack(const T& val)
	{
		if (this->capacity == this->size)
			return;

		p[this->size] = val;
		this->size++;
	}
	//尾删
	void popmyBack()
	{
		if (this->size == 0)
			return;

		this->size--;
	}
	//获取数组长度
	int getSize()
	{
		return this->size;
	}

	~MyArr()
	{
		if (p != NULL)
		{
			delete[] p;
			p = NULL;
		}

	}
private:
	T* p;
	int capacity;
	int size;
};

//学生类
template<class Nametype,class Agetype>
class Student
{
public:
	Student()
	{

	}
	Student(Nametype name, Agetype age)
	{
		this->name = name;
		this->age = age;
	}
public:
	Nametype name;
	Agetype age;
};


void printMyarr(MyArr<Student<string, int>> arr,int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << endl;
	}
}

void test01()
{
	MyArr<Student<string,int>> arr(4);
	Student<string, int> s1("aaa", 11);
	Student<string, int> s2("bbb", 22);
	Student<string, int> s3("ccc", 33);
	Student<string, int> s4("ddd", 44);
	arr.pushmyBack(s1);
	arr.pushmyBack(s2);
	arr.pushmyBack(s3);
	arr.pushmyBack(s4);
	int len = arr.getSize();
	printMyarr(arr, len);
	cout << "----------------------" << endl;
	arr.popmyBack();
	len = arr.getSize();
	printMyarr(arr, len);
}

int main()
{
	test01();

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值