IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++之泛型编程 -> 正文阅读

[C++知识库]C++之泛型编程

目录

模板

模板的特点

函数模板

前言

函数模板的使用方式

函数模板具体案例

使用模板的注意事项

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

具体案例

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

模板的局限性

具体化模板

类模板

前言

类模板与函数模板的区别

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

类模板对象做函数的参数

传入方式

具体案例

类模板与继承

前言

子类指定具体类型案例

子类不指定具体类型案例

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

类模板分文件编写

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);
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 10:47:31  更:2022-12-25 10:48:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 6:03:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码