• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
LOFLY
终其一生,编织快乐
博客园    首页    新随笔    联系   管理    订阅  订阅

C++ 之函数模板

C++ 之函数模板

函数的参数类型不确定,这样就可以使用泛型。

// 类型参数化  C++中称之为泛型编程--模板技术
template <class T > // 告诉编译器,下面如果出现T不要报错,T是一个通用类型  这里将class替换成typename是一样的
void mySwap(T &a, T &b) {
	T tmp = a;
	a = b;
	b = tmp;
}

自动类型推导,函数必须有参数才可以推导

// 2.显示调用
	mySwap<int>(a, b); // 这里指定T是int类型
	cout << "a=" << a << "," << "b=" << b << endl;
// 模板必须可以推导出T才可以使用 ,要么直接显示指定T

template < typename T>
void mySwap1() {
}
mySwap1 < double >(); // 如果不能根据参数列表推出来,就得显示指定类型

mySwap(a,b) // 根据a,b的类型自动推导T的类型。
mySwap<int> // (a,b)直接指定T的类型是int

2 使用函数模板实现通用数组排序

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


// 使用通用类型 对int char类型的数组进行排序
// 排序的规则  降序  使用选择排序

// 选择排序 每1趟中从待排序中选择一个最小的放在已排序的右端
template<class T>
void mySort(T arr[],int len) {  

	for (int i = 0; i < len-1;i++) {

		int max = i;
		for (int j = i + 1; j < len;j++) {
			if (arr[max] < arr[j]) {
				
				// 交换下标
				max = j;
			}
		}
		if (max != i) {
			// 交换数据
			swap(arr[max],arr[i]);
		}
	}
}

//输出数组元素的模板
template<class T>
void printArray(T  arr[],int len) {
	for (int i = 0; i < len;i++) {
		cout << arr[i] << "\t";
	}
}
void test01() {
	char arr[] = "chengluofei";
	//int len = sizeof(arr) / sizeof(char);
	mySort(arr,strlen(arr));
	printArray(arr, strlen(arr));
}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

3 普通函数与函数模板的区别以及调用规则

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//1. 普通函数与函数模板的区别
template<class T>
T myPlus(T a,T b) {
	return a + b;
}

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

void test01() {
	int a = 10;
	int b = 20;
	cout << myPlus(a, b) << endl;
	cout <<  myPlus2(a, b) << endl;
	int c = 'c';

	//cout << myPlus(a, c) << endl; // 类型推导出错

	// char类型自动转int 类型 1. 普通的函数可以隐式类型转换  函数模板不可以进行隐式类型转换
	cout << myPlus2(a, c) << endl; 
}

// 2. 普通函数和函数模板的调用规则
template<class T>
void myPrint(T a, T b) 
{
	cout << "模板调用的myPrint" << endl;
}

template<class T>
void myPrint(T a, T b,T c)
{
	cout << "模板调用的myPrint(a,b,c)" << endl;
}

void myPrint(int a, int b)
{
	cout << "函数调用的myPrint" << endl;
}

void test02() {

	int a = 10;
	int b = 20;

	// 1.如果出现重载(普通函数和函数模板函数名与参数个数相同),优先调用普通的函数  如果没有实现普通函数 会出错
	//myPrint(10,20);

	// 2.如果想强制调用函数模板 可以使用空菱形列表
	myPrint<>(10,20);

	// 3.函数模板可以发生重载
	myPrint<>(10, 20,30);

	// 4. 如果函数模板可以优先产生更好的匹配,那么优先调用函数模板
	char c = 'c', d = 'd';
	myPrint(c,d);
}

int main()
{
	test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

4 模板机制

函数模板通过不同的类型产生不同的函数。 编译器会对函数模板进行两次编译。在声明的地方对模板本身进行编译, 在调用的时候对参数进行替换后再进行一次编译。 STL 90%以上都是使用模板来做的。 通过模板产生的函数就叫模板函数。(为函数模板传值时,会重新生成对应类型的函数。) 模板并不是万能的,不能通用所有的数据类型。 模板不能直接调用, 生成后的模板函数才可以调用。 二次编译,第一次对模板进行编译,第二次对替换T类型后的函数进行编译。

5 函数模板的局限性

template < class T > void f(T a, T b){ …. } 如果T是数组的话, 就没办法使得a = b语句成立。if(a > b) 语句也不能成立。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <string>
#include <vector>


class Person {
public:
	Person(string name, int age) :m_Name(name), m_Age(age) {

	}

	string m_Name;
	int m_Age;
	
};

template <class T>
bool myCompare(T &a ,T &b) {

	if (a == b) {
		return true;
	}
	else return false;
}
// 通过具体化自定义数据类型,解决上述问题
// 如果具体化能够优先匹配,那么就选择具体化
// 语法 template <> 函数的声明和实现 <具体类型>
template<> bool  myCompare<Person>(Person &a, Person &b) {
	if (a.m_Age == b.m_Age) {
		return true;
	}
	else return false;
}

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

	bool res = myCompare(a,b);
	cout << res << endl;

	Person p1("Tom",3);
	Person p2("Jerry",4);

	bool res2 = myCompare(p1, p2); // 匹配具体化的模板
	cout << res2 << endl;
}

int main()
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

6 类模板的基本使用

// 类模板 
template <class NameType,class AgeType>
class Person {

public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson() {
		cout <<"姓名:"<<this->m_Name <<"  年龄:"<< this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};

类模板支持默认参数的(默认的类型),函数模板不可以。
template <class NameType = string ,class AgeType= int>
如果这里声明了默认的类型,那么创建对象的时候,不需要在菱形中显示声明类型。
Person <> p("张三",12);

7 成员函数的创建时机

class Person1 {
public:
	void showPerson1() {
		cout << "Person1的调用" << endl;
	}
};

class Person2 {
public:
	void showPerson1() {
		cout << "Person2的调用" << endl;
	}
};

template <class T>
class Myclass {

public:

	T obj;
	void func1() {
		obj.showPerson1(); 
	}

	void func2() {
		obj.showPerson2();
	}
};

// 成员函数一开始不会创建出来, 而是在运行时才去创建
void test02() {

	Myclass<Person1> mc;
	mc.func1();
	mc.func2(); // 报错
}

8 类模板做函数的参数

// 1. 指定传入类型
void doWark(Person<string,int> &p) { // 指定传入类型
	p.showPerson();
}
// 2.参数模板化
template<class T1,class T2>
void doWork2(Person<T1,T2> & p) {
	// 如何查看类型
	cout << typeid(T1).name() << endl;
	cout << typeid(T2).name() << endl;
	p.showPerson();
}

void test01() {
	Person<string,int> p("张三",23);
	doWark(p);
	doWork2(p);
}


// 3 整体类型化
template <class T>
void doWork3(T &p) {
	p.showPerson();
}

void test02() {
	Person<> p("孙悟空",500);
	doWork3(p);
}

9 类模板遇到继承问题以及解决办法

子类继承父类(此时父类是模板类)的时候,需要指定父类模板中的类型,不然创建子类对象的时候, 会默认调用父类的构造器,导致父类无法给未知类型的变量分配内存。

template <class T>
class Base {
public:
	T m_A;
};
// 让child继承  必须告知base中的T类型 , 否则T无法分配内存
class Child : public Base<int> {

};
template <class T1, class T2>
class Child2 : public Base<T2> {

public:
	Child2() {
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
	T1 m_B;
};
void test01() {
	Child2<int, double> child; // 由用户指定类型
}

10 类模板类外实现成员函数

template <class T1,class T2>
class Person {
public:
	Person(T1 name, T2 age);

	void showPerson();

private:
	T1 m_name;
	T2 m_age;
};

template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_name = name;
	this->m_age = age;
}

template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名:" << this->m_name << ", 年龄:" << this->m_age << endl;
}

11 类模板分文件编写以及问题解决

image

运行后,保持解决方法: 将Person.h 改为 Person.cpp
分文件写的话,因为是模板函数,代码一开始并不成成模板函数,因为确定不了类型。(C++是采用单元编译的。)
Person.h文件:

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

template <class T1, class T2>
class Person {
public:
	Person(T1 name, T2 age);
	void showPerson();

private:
	T1 m_name;
	T2 m_age;
};

编译的时候, 编译检查Person.h文件,语法没有任何问题。 接着,检查Person.cpp文件。

#include "Person.h"
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_name = name;
	this->m_age = age;
}

template <class T1, class T2>
void::showPerson() {
	cout << "姓名:" << this->m_name << ", 年龄:" << this->m_age << endl;
}

因为Person.cpp文件里面全是函数目版,编译器不生成模板函数,所以对应的Person.h中的具体实现也就实现不了。所以 在主函数里面,直接导入对应的cpp文件就可解决上面问题。

建议模板不要做分文件编写,一般写到一个类即可,全写到一个文件Person.h文件中,类内声明,类内实现。再把.h后缀改为.hpp.

main.cpp文件:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "Person.hpp"

void test() {
	Person<string, int> p1("张三",12);
	p1.showPerson();
}

int main() {
	
	test();
	system("pause");
	return EXIT_SUCCESS;
}

.hpp文件一般是写模板用的。
image

12 友元函数碰到类模板-友元函数类内实现

如果函数和普通函数都能匹配到的话,优先调用普通函数。

template <class T1, class T2>
class Person {

	// 友元函数类内实现  默认是全局函数
	friend void printPerson(Person<T1, T2> &p); // 普通函数的声明
public:
	Person(T1 name, T2 age) {
		this->name = name;
		this->age = age;
	}
private:
	T1 name;
	T2 age;

};

template <class T1, class T2>
void printPerson(Person<T1, T2> &p) {
	cout << "姓名:" << p.name << "年龄" << p.age << endl;
}

void test() {
	Person<string, int> p("张三", 12);
	printPerson(p); // 报错  无法解析外部命令
}

解决方案: 在友元函数函数名处加上菱形括号,表示模板函数的声明。

// 让编译器提前看到printPerson的声明
// 让编译器看到Person类的声明
template <class T1, class T2> class Person;
template <class T1, class T2> void printPerson(Person<T1, T2> &p);


template <class T1, class T2>
class Person {

	// 友元函数类内实现  默认是全局函数
	//friend void printPerson(Person<T1, T2> &p); // 普通函数的声明
	friend void printPerson<>(Person<T1, T2> &p); // 模板函数的声明
public:
	Person(T1 name, T2 age) {
		this->name = name;
		this->age = age;
	}
private:
	T1 name;
	T2 age;

};

template <class T1, class T2>
void printPerson(Person<T1, T2> &p) {
	cout << "姓名:" << p.name << "年龄" << p.age << endl;
}

void test() {

	Person<string, int> p("张三", 12);
	printPerson(p);
}

int main()
{
	test();
	system("pause");
	return EXIT_SUCCESS;
}

13 类模板的应用-数组类封装

MyArray.hpp文件

template <class T>
class MyArray {
public:

	explicit MyArray(int capacity) { // 防止隐式类型转换 MyArray arr = 10;
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];
	}

	MyArray(const MyArray & arry) {
		this->m_Capacity = arry.m_Capacity;
		this->m_Size = arry.m_Size;
		this->pAddress = new T[this->m_Capacity];

		for (int i = 0; i < this->m_Size;i++) {
			this->pAddress[i] = arry[i];
		}
	}

	~MyArray() {
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}
	}

	// 赋值操作符重载
	void operator=(MyArray & arry) {

		// 先判断原始数据 有 就清空
		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->pAddress = NULL;
		}

		this->m_Capacity = arry.m_Capacity;
		this->m_Size = arry.m_Size;
		this->pAddress = new T[this->m_Capacity];

		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arry[i];
		}

	}

	// [] 重载
	T & operator[](int index){
		return this->pAddress[index];
	}

	// 尾插法
	void pushBack(T  val) {

		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	// 获取大小
	int getSize() {

		return this->m_Size;
	}

	// 输出自身数据的方法
	void printArry() {
		for (int i = 0; i < this->m_Size;i++) {
			cout << this->pAddress[i] << " ";
		}
		cout << endl;
	}

private:
	T * pAddress;
	int m_Capacity;
	int m_Size;
};

主函数.cpp文件:

class Person {
public:
	Person() {
		cout << "调用默认的构造函数" << endl;
	}

	Person(string name,int age) {
		this->m_Name = name;
		this->m_age = age;
	}

	~Person() {

		cout << "调用析构函数" << endl;
	}
	string m_Name;
	int m_age;
};

void printPersonArray(MyArray<Person> &arr) {

	for (int i = 0; i < arr.getSize();i++) {
		cout << "姓名:" << arr[i].m_Name << " 年龄" << arr[i].m_age << endl;
	}
}

void test() {
	//MyArray<int> arr(10);

	//for (int i = 0; i < 10;i++) {
	//	arr.pushBack(i+100);
	//}
	//arr.printArry();

	/*MyArray<float> arr(10);

	for (int i = 0; i < 10;i++) {
		arr.pushBack(0.1f+i);
	}
	arr.printArry();
	cout << arr[5] << endl;*/

	Person p1("张三",12);
	Person p2("李四", 13);
	Person p3("王五", 14);
	Person p4("赵六", 15);

	MyArray<Person> pArray(10);
	pArray.pushBack(p1);
	pArray.pushBack(p2);
	pArray.pushBack(p3);
	pArray.pushBack(p4);

	printPersonArray(pArray);

}
int main()
{
	test();
	system("pause");
	return EXIT_SUCCESS;
}
posted @ 2022-08-28 09:44  编织快乐  阅读(57)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3