C++之泛型编程
创始人
2024-04-30 03:24:17

目录

模板

模板的特点

函数模板

前言

函数模板的使用方式

函数模板具体案例

使用模板的注意事项

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

具体案例

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

模板的局限性

具体化模板

类模板

前言

类模板与函数模板的区别

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

类模板对象做函数的参数

传入方式

具体案例

类模板与继承

前言

子类指定具体类型案例

子类不指定具体类型案例

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

类模板分文件编写

1.直接包含源文件

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

类模板与友元

模板

前言:

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

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

模板的特点

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

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

函数模板

前言

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

语法:

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

解释:

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

函数模板的使用方式

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

函数模板具体案例

#define _CRT_SECURE_NO_WARNINGS 1
#include 
using namespace std;
//函数模板
template 
//通用交换函数模板
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;//显式指定类型(为模板参数)mySwap(a,b);cout << "a:" << a << "\tb:" << b << endl;
}

使用模板的注意事项

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

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

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

具体案例

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

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

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

void myPrint(int a, int b) {cout << "调用普通函数" << endl;
}
template 
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 
void myPrint(T a, T b) {cout << "调用函数模板" << endl;
}
void main() {int a = 10;int b = 20;//通过空模板的参数列表强制调用函数模板myPrint<>(a, b);
}

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

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

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

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

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

模板的局限性

template 
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 
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

解释:

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

类模板与函数模板的区别

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

template
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 p("叶秋", 25);cout << "p的name:" << p.m_Name << endl << "p的age:" << p.m_Age << endl;
}

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

//int为类模板的默认参数
template
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 p("叶秋", 25);cout << "p的name:" << p.m_Name << endl << "p的age:" << p.m_Age << endl;
}

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

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

类模板对象做函数的参数

传入方式

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

具体案例

template
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 &p) {p.showPerson();
}
//参数模板化
template
void printPerson2(Person &p) {p.showPerson();
}
//整个类模板化
template
void printPerson3(P &p) {p.showPerson();
}
void main() {Person p("孙悟空", 100);printPerson1(p);printPerson2(p);printPerson3(p);
}

类模板与继承

前言

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

子类指定具体类型案例

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

子类不指定具体类型案例

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

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

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

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

类模板分文件编写

1.直接包含源文件

person.h文件内

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

person.cpp文件内

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

执行文件内

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

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

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

person.hpp文件内

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

执行文件内

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

类模板与友元

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

template
class Person {//全局函数类内实现friend void printPerson(Person 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 p("lili", 18);printPerson(p);
}

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

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

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...