C++之泛型编程

本文深入探讨C++的模板机制,包括函数模板和类模板的使用、区别及注意事项。函数模板允许创建通用函数,类模板则用于构建通用类。内容涵盖模板特点、自动类型推导、模板具体化、类模板成员函数的创建时机、类模板与继承、友元等关键概念,旨在提升代码复用性和泛型编程能力。
摘要由CSDN通过智能技术生成

目录

模板

模板的特点

函数模板

前言

函数模板的使用方式

函数模板具体案例

使用模板的注意事项

普通函数与函数模板间的区别

具体案例

普通函数与函数模板调用规则

模板的局限性

具体化模板

类模板

前言

类模板与函数模板的区别

类模板中成员函数创建时机

类模板对象做函数的参数

传入方式

具体案例

类模板与继承

前言

子类指定具体类型案例

子类不指定具体类型案例

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

类模板分文件编写

1.直接包含源文件

2.将.h和.cpp文件中的内容写到一起,后将后缀名改为.hpp文件

类模板与友元

模板

前言:

  • C++中相对于面向对象的另一种编程思想就是泛型编程,主要利用的技术就是模板
  • C++提供了两种模板机制(函数模板和类模板)

概念:模板就是建立通用的模具,大大提高复用性

模板的特点

  • 模板不可以直接使用,它只是一个框架
  • 模板通用性很强,但是并不是万能的

使用模板的目的:提高复用性,将类型参数化 

函数模板

前言

作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表

语法:

//声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用的数据类型
template<typename T>
函数声明或函数定义

解释:

  • template:声明创建模板
  • typename:表明其后面的符号为一种数据类型(该关键字也可以用class代替)
  • T:通用的数据类型;名称可以替换,通常为大写字母。

函数模板的使用方式

  • 自动类型推导:向交换模板函数中传递的参数的类型会被自动解析
  • 显示指定类型:指定模板的具体数据类型

函数模板具体案例

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
//函数模板
template <typename T>
//通用交换函数模板
void mySwap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
void main() {
	int a = 10;
	int b = 20.0;
	//自动类型推导(a和b的数据类型会被自动解析)
	mySwap(a, b);
	cout << "a:" << a << "\tb:" << b << endl;
	//显式指定类型(<int>为模板参数)
	mySwap<int>(a,b);
	cout << "a:" << a << "\tb:" << b << endl;
}

使用模板的注意事项

  • 在函数中,若多个参数使用模板,那么自动类型推导必须推导出一致的数据类型才可以使用
  • 模板必须确定出T的数据类型才可以使用

普通函数与函数模板间的区别

普通函数调用时可以发生自动类型转换(隐式类型转换);函数模板调用时,若利用自动类型推导,则不会发生隐式类型转换,但若利用显式指定类型的方式则可以发生隐式类型转换

具体案例

#include <iostream>
using namespace std;
template <typename T>
T myAdd(T a, T b) {
	return a + b;
}
void main() {
	int a = 10;
	char c = 'c';
	//自动类型推导
	//cout << myAdd(a, c) << endl;//报错,参数类型的指定必须一致,不能自动类型转换
	//隐式指定类型
	cout << myAdd<int>(a, c) << endl;
	//明确说明T的类型为int,传入的参数是int类型直接传,不是int类型则使用隐式类型转换转为int类型
}

普通函数与函数模板调用规则

1.若函数模板与普通函数都可以调用,则优先调用普通函数

void myPrint(int a, int b) {
	cout << "调用普通函数" << endl;
}
template <typename T>
void myPrint(T a, T b) {
	cout << "调用函数模板" << endl;
}
void main() {
	int a = 10;
	int b = 20;
	myPrint(a, b);//调用普通函数
}

2.可以通过空模板参数列表的方式来强制调用函数模板

//只写了函数声明,没写函数实现
void myPrint(int a, int b);
template <typename T>
void myPrint(T a, T b) {
	cout << "调用函数模板" << endl;
}
void main() {
	int a = 10;
	int b = 20;
	//通过空模板的参数列表强制调用函数模板
	myPrint<>(a, b);
}

3.模板也可以实现函数重载

template <typename T>
void myPrint(T a, T b) {
	cout << "调用函数模板1" << endl;
}
//重载函数模板
template <typename T>
void myPrint(T a, T b, T c) {
	cout << "调用函数模板2" << endl;
}
void main() {
	int a = 10;
	int b = 20;
	int c = 30;
	//调用函数模板1
	myPrint(a, b);
	//调用函数模板2
	myPrint(a, b, c);
}

4.如果函数模板可以产生更好的匹配,则优先使用函数模板

void myPrint(int a, int b) {
	cout << "调用普通函数" << endl;
}
template <typename T>
void myPrint(T a, T b) {
	cout << "调用函数模板" << endl;
}
void main() {
	char a = 'a';
	char b = 'b';
	//调用函数模板
	myPrint(a, b);
}

注意:若提供了函数模板,那么最好就不要提供普通函数,否则容易出现二义性

模板的局限性

template <class T>
void func(T a, T b) {
	a = b;
}

注意:模板并不是万能的,该模板函数有一个局限性,若传入的a和b是一个数组,那么就无法实现了

具体化模板

class Person {
public:
	Person(string name, int age) {
		this->m_Name = name;
		this->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<>)的Person版本来实现代码,具体化优先调用
template<> bool myCompare(Person & a, Person & b) {
	if (a.m_Age == b.m_Age && a.m_Name == b.m_Name) {
		return true;
	}
	else {
		return false;
	}
}
void main() {
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	bool ret = myCompare(p1, p2);
	if (ret) {
		cout << "p1==p2" << endl;
	}
	else {
		cout << "p1!=p2" << endl;
	}
}

解释:模板的类型若为Person类型,那么自动调用具体化了的方法 

类模板

前言

作用:建立一个通用的类,类中的成员,数据类型可以不具体指定,用一个虚拟的类型来代表

语法: 

template<typename T>
类

解释:

  • template:声明创建模板
  • typename:表明其后面带符号的是一种数据类型(也可以用class替换)
  • T:通用的数据类型,名称可以替换,通常为大写字母

类模板与函数模板的区别

1.类模板没有自动类型推导的使用方式

template<class NameType,class AgeType>
class Person {
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	NameType m_Name;
	AgeType m_Age;
};
void main() {
	//类模板的使用(显式指定类型)
	Person<string, int> p("叶秋", 25);
	cout << "p的name:" << p.m_Name << endl << "p的age:" << p.m_Age << endl;
}

2.类模板在模板参数列表中可以有默认参数

//int为类模板的默认参数
template<class NameType,class AgeType=int>
class Person {
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	NameType m_Name;
	AgeType m_Age;
};
void main() {
	//类模板默认参数的使用
	Person<string> p("叶秋", 25);
	cout << "p的name:" << p.m_Name << endl << "p的age:" << p.m_Age << endl;
}

类模板中成员函数创建时机

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才可以创建

类模板对象做函数的参数

传入方式

  • 指定传入类型——直接显示对象的数据类型
  • 参数模板化——将对象中的参数变为模板进行传递
  • 整个类模板化——将这个对象类型模板化进行传递

具体案例

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 << "\t年龄:" << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};
//指定传入类型
void printPerson1(Person<string,int> &p) {
	p.showPerson();
}
//参数模板化
template<class T1 ,class T2>
void printPerson2(Person<T1,T2> &p) {
	p.showPerson();
}
//整个类模板化
template<class P>
void printPerson3(P &p) {
	p.showPerson();
}
void main() {
	Person<string, int> p("孙悟空", 100);
	printPerson1(p);
	printPerson2(p);
	printPerson3(p);
}

类模板与继承

前言

  • 当子类继承的父类是一个类模板时,子类在声明的时候需要指定父类的类型
  • 若不指定具体的类型,那么编译器无法给予子类分配内存
  • 若想灵活指定父类中T的类型,子类也需要变成类模板

子类指定具体类型案例

template<class T>
class Base {
	T m;
};
class Son:public Base<int> {};

子类不指定具体类型案例

template<class T>
class Base {
	T m;
};
template<class T>
class Son:public Base<T> {};

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

template<class NameType,class AgeType>
class Person {
public:
	Person(NameType name, AgeType age);
	void showPerson();
	NameType m_Name;
	AgeType m_Age;
};
//构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType,AgeType>::Person(NameType name,AgeType age){
	this->m_Name = name;
	this->m_Age = age;
}
//成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson() {
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

注意:类模板中的成员函数类外实现时需要加上模板的参数列表

类模板分文件编写

1.直接包含源文件

person.h文件内

#pragma once
#include <iostream>
using namespace std;
template<class NameType, class AgeType>
class Person {
public:
	Person(NameType name, AgeType age);
	void showPerson();
	NameType m_Name;
	AgeType m_Age;
};

person.cpp文件内

#include "person.h"
//构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age) {
	this->m_Name = name;
	this->m_Age = age;
}
//成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson() {
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

执行文件内

//这里必须包含.cpp文件方可
#include "person.cpp"
void main() {
	Person<string, int> p("lili", 18);
	p.showPerson();
}

执行文件不可以包含.h文件必须包含.cpp问件原因:类模板中的成员函数在调用时才可以创建

2.将.h和.cpp文件中的内容写到一起,后将后缀名改为.hpp文件

person.hpp文件内

#pragma once
#include <iostream>
using namespace std;
template<class NameType, class AgeType>
class Person {
public:
	Person(NameType name, AgeType age);
	void showPerson();
	NameType m_Name;
	AgeType m_Age;
};

//构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age) {
	this->m_Name = name;
	this->m_Age = age;
}
//成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson() {
	cout << "姓名:" << this->m_Name << "\t年龄:" << this->m_Age << endl;
}

执行文件内

//这里必须包含.hpp文件
#include "person.hpp"
void main() {
	Person<string, int> p("lili", 18);
	p.showPerson();
}

类模板与友元

1.全局函数类内实现,直接在类内声明友元即可

template<class T1,class T2>
class Person {
	//全局函数类内实现
	friend void printPerson(Person<T1,T2> p) {
		cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
	}
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}
private:
	T1 m_Name;
	T2 m_Age;
};
void main() {
	Person<string, int> p("lili", 18);
	printPerson(p);
}

2.全局函数类外实现,需要让编译器知道该全局函数的存在

//编译器需要先知道printPerson函数的存在以及Person类的存在
template< class T1, class T2 >
class Person;
template<class T1, class T2>
void printPerson(Person< T1, T2 > p) {
	cout << "姓名:" << p.m_Name << "\t年龄:" << p.m_Age << endl;
}
template<class T1,class T2>
class Person {
	//全局函数类外实现
	//加空模板参数列表证明他是个模板函数
	//若全局函数是类外实现,需要让编译器提前知道这个函数的存在
	friend void printPerson<>(Person<T1, T2> p);
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}
private:
	T1 m_Name;
	T2 m_Age;
};
void main() {
	Person<string, int> p("lili", 18);
	printPerson(p);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值