1.2.1 函数模板语法
作用:建立一个通用函数,其函数返回值 和 形参类型可以不具体指定,用一个 虚拟的类型 来代表。
语法:
template
函数声明或定义
解释:
template---声明创建模板
typename---表示后面的符号是一种数据类型,也可以用class
T---通用的数据类型
示例:
#include
using namespace std;
//函数模板
//整型交换函数
void swapInt(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
//浮点型交换函数
void swapFloat(float a, float b) {
float temp = a;
a = b;
b = temp;
}
void test01() {
int a = 10;
int b = 20;
swapInt(a, b);
cout << "a = "<< a << endl;
cout << "b = " << b << endl;
float c = 1.5;
float d = 2.3;
swapFloat(c, d);
cout << "c = " << c << endl;
cout << "d = " << d << endl;
}
问题:只有形参的类型不同,函数体内部过程完全相同
解决:
//函数模板:将类型参数化
template//声明模板,告诉编译器T不要报错,它是一个通用数据类型
void swapT(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
void test01() {
int a = 10;
int b = 20;
//两种方式使用模板
//1、自动类型推导
swapT(a, b);//自动推导a,b是int型
//2、显式
swapT(a, b);
cout << "a = "<< a << endl;
cout << "b = " << b << endl;
}
template
void swapT(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
//1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01() {
int a = 1;
int b = 2;
swapT(a, b); //正确
char c = 'c';
// swapT(a, c); 错误,推导出T是int 和 char
}
//2、模板必须要确定T的数据类型,才可以使用
template
void func()
{
cout << "func调用" << endl;
}
void test02() {
func();//此时必须显示,告诉编译器T是什么
}
案例描述:
//交换模板
template
void swapT(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
//实现通用的排序函数,规则是从大到小
template
void mySort(T arr[], int len) {
for (int i = 0; i < len ; i++)
{
int max = i;
for (int j = i+1; j < len; j++) {
if (arr[j] > arr[max])
{
max = j;
}
}
if (max != i)
{
swap(arr[i], arr[max]);
}
}
}
//打印数组的模板
template
void print_Arr(T arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i]<<" ";
}
cout << endl;
}
void test01() {
char charArr[] = "bcdeja";
int num = sizeof(charArr) / sizeof(char);
mySort(charArr,num);
print_Arr(charArr, num);
}
void test02()
{
int intArr[] = { 7,1,3,4,2,5 };
int num = sizeof(intArr) / sizeof(int);
mySort(intArr, num);
print_Arr(intArr, num);
}
示例:
//普通函数
int myAdd1(int a, int b)
{
return a + b;
}
//函数模板
template
T myAdd2(T a, T b)
{
return a + b;
}
void test1() {
int a = 10;
char c = 'c';
cout << myAdd1(a, c) << endl; //普通函数调用,char自动转换为int型
//cout << myAdd2(a, c) << endl; //错误,自动类型推导时,T不可以进行隐式转换
cout << myAdd2(a, c) << endl; //显式指定参数类型,可以隐式转换
}
void myPrint(int a, int b) {
cout << "调用的普通函数" << endl;
}
template
void myPrint(T a, T b)
{
cout << "调用的模板" << endl;
}
//3、函数模板也可以重载
template
void myPrint(T a, T b,T c)
{
cout << "重载的模板" << endl;
}
void test1()
{
int a = 10;
int b = 20;
//1、如何函数模板和普通函数都可以实现,优先调用普通函数
myPrint(a, b); //输出 普通函数调用
//2、可以通过空模板参数列表来强制调用函数模板
myPrint<>(a, b); //强制调用函数模板
//函数模板可以重载
myPrint(a, b, 100);//调用重载的模板
//如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
myPrint(c1, c2);
//调用普通函数时,int可以隐式转换为char
//调用函数模板时,自动推导T为char ,更直接
}
说明:既然提供了函数模板,最好不要提供普通函数,否则容易出现二义性
问题:
template
void f(T a, T b)
{
a = b; //若赋值操作 传入的a,b是数组,则无法实现
}
template
void f(T a, T b)
{
if (a > b) { //若是自定义的对象,则无法比较大小
...
}
}
解决:
C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
class Person {
public:
Person(string name, int age)
{
m_name = name;
m_age = age;
}
string m_name;
int m_age;
};
template
bool myCompare(T &a, T &b)
{
if (a == b){
return true;
}
else{
return false;
}
}
//利用具体化的Person版本,具体化优先调用
template<> bool myCompare(Person &a, Person &b)
{
if (a.m_name==b.m_name&&a.m_age==b.m_age) {
return true;
}
else {
return false;
}
}
void test1() {
Person p1("Tom", 10);
Person p2("Jack", 20);
cout << myCompare(p1, p2);
}
总结:
作用:建立一个通用类,类中的成员、数据类型可以不具体指定,用一个虚拟的类型来代表
语法:
template
类
例:
template
class Person {
public:
Person(NameType name, AgeType age) {
m_name = name;
m_age = age;
}
void show_per() {
cout << m_name << endl;
cout << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test1() {
Person p1("张三", 18);
p1.show_per();
}
template
class Person {
public:
Person(NameType name, AgeType age) {
m_name = name;
m_age = age;
}
void show_p() {
cout << m_name << endl;
cout << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test1() {
//1、,类模板无法用自动类型推导,必须显式指定类型
//Person p1("张三", 18); 错误
Person p2("孙悟空", 999);
p2.show_p();
}
template //默认参数类型
class Person {
public:
Person(NameType name, AgeType age) {
m_name = name;
m_age = age;
}
void show_p() {
cout << m_name << endl;
cout << m_age << endl;
}
NameType m_name;
AgeType m_age;
};
//2、类模板在模板参数列表中可以有默认参数
void test2() {
Person p1("孙悟空", 999);
p1.show_p();
}
class Person1 {
public:
//普通类的成员函数
void show_p1() {
cout << "Person1 的调用"<< endl;
}
};
class Person2 {
public:
void show_p2() {
cout << "Person2 的调用" << endl;
}
};
template
class MyClass {
public:
T obj;
//类模板中的成员函数编译时不会创建,因为无法确定obj的数据类型
//直到被调用的时候,确定了T的数据类型,才会调用
void func1() {
obj.show_p1();
}
void func2() {
obj.show_p2();
}
};
void test1()
{
MyClass m; //此时确定了T的类型,是Person1
m.func1();
//m.func2(); 错误,func2不是Person1的成员函数
}
含义:类模板实例化出的对象,像函数传参的方式
有三种传入方式:
//类模板对象做函数参数
template
class Person {
public:
Person(T1 name, T2 age)
{
m_name = name;
m_age = age;
}
void show_p() {
cout << m_name << endl;
cout << m_age << endl;
}
T1 m_name;
T2 m_age;
};
//1、指定传入类型
void print_p1(Person &p1) {
p1.show_p();
}
void test1() {
Person p1("张三", 18);
print_p1(p1);
}
//2、参数模板化
template
void print_p2(Person &p2) {
p2.show_p();
//查看模板参数 推导的类型
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
}
void test2()
{
Person p2("猪八戒", 90);
print_p2(p2);
}
//3、整个类模板化
template
void print_p3(T &p3) {
p3.show_p();
cout << typeid(T).name();
}
void test3()
{
Person p3("唐僧", 999);
print_p3(p3);
}
使用广泛的是第一种:指定传入的参数类型
当类模板碰到继承时,需要注意:
//类模板与继承
template
class Base {
public:
T m;
};
// class Son1:public Base {}; //错误,必须知道T的数据类型,才能继承给子类
class Son1 :public Base {
};
//如果想灵活指定父类中的T 的类型,子类也要变成类模板
template
class Son2 :public Base {
T1 obj;
};
void test1() {
Son1 s1;
Son2 s2; //明确指出数据类型
}
总结:如果父类是类模板,子类需要指定父类T中的数据类型
template
class Person {
public:
Person(T1 name, T2 age);
/*{
m_name = name;
m_age = age;
}*/
void show_p();
/*{
cout << "m_name = " << m_name << endl;
cout << "m_age = " << m_age << endl;
}*/
T1 m_name;
T2 m_age;
};
//构造函数的类外实现
template
Person::Person(T1 name, T2 age) //是类模板的类外实现
{
m_name = name;
m_age = age;
}
//成员函数的类外实现
template
void Person::show_p()
{
cout << "m_name = " << m_name << endl;
cout << "m_age = " << m_age << endl;
}
void test1() {
Person p1("张三", 18);
p1.show_p();
}
问题:类模板中成员函数创建时机是在 调用阶段,导致分文件编写时链接不到
解决1:直接包含.cpp源文件
//person.h
#pragma once
#include
#include
using namespace std;
//类模板分文件编写问题以及解决
template
class Person {
public:
Person(T1 name, T2 age);
void show_p();
T1 m_name;
T2 m_age;
};
//由于类模板中的成员函数不创建,当编译器看到这些代码时,并不会生成函数
//person.cpp
#include"person.h"
//构造函数的类外实现
template
Person::Person(T1 name, T2 age) //是类模板的类外实现
{
m_name = name;
m_age = age;
}
//成员函数的类外实现
template
void Person::show_p()
{
cout << "m_name = " << m_name << endl;
cout << "m_age = " << m_age << endl;
}
//主函数
#include
#include
using namespace std;
//#include "perosn.h" 报错
//第一种解决方式:直接包含源文件
#include "person.cpp" //正确运行
//因为里面只有类模板的成员函数声明,没有创建
//而.cpp文件中有成员函数的具体实现,且.cpp中也包含.h文件
//第二种解决方式:.h和.cpp写在一起,后缀名为.hpp文件,约定象征类模板
void test1() {
Person p1("张三", 18);
p1.show_p();
}
解决2(主流):将声明和实现写到同一个文件中,并更改后缀名为.hpp
//person.hpp
#pragma once
#include
#include
using namespace std;
//类模板分文件编写问题以及解决
template
class Person {
public:
Person(T1 name, T2 age);
void show_p();
T1 m_name;
T2 m_age;
};
//构造函数的类外实现
template
Person::Person(T1 name, T2 age) //是类模板的类外实现
{
m_name = name;
m_age = age;
}
//成员函数的类外实现
template
void Person::show_p()
{
cout << "m_name = " << m_name << endl;
cout << "m_age = " << m_age << endl;
}
//主函数
#include "person.hpp"
//第二种解决方式:.h和.cpp写在一起,后缀名为.hpp文件,约定象征类模板
void test1() {
Person p1("张三", 18);
p1.show_p();
}
全局函数类内实现:直接在类内声明友元即可
template
class Person {
//全局函数,类内实现
friend void print_p(Person p) {
cout << p.m_name << endl;
cout << p.m_age << endl;
}
public:
Person(T1 name,T2 age) {
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
void test1() {
Person p1("张三",18);
print_p(p1);
}
全局函数类外实现:需要提前让编译器知道全局函数的存在
template //告诉编译器,这有一个模板类
class Person;
//全局函数,类外实现,写在前面,提前告诉编译器
template
void print_p(Person p)
{
cout << p.m_age << endl;
cout << p.m_name << endl;
}
template
class Person {
//全局函数,类外实现
friend void print_p<>(Person p);
public:
Person(T1 name,T2 age) {
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
void test1() {
Person p1("张三",18);
print_p(p1);
}
总结:全局函数在类内实现更方便,用法也更简单,且编译器可以直接识别
描述:实现一个通用的数组类,要求如下:
#pragma once
#include
#include
using namespace std;
template
class MyArray {
public:
//构造函数
MyArray(int capacity) {
//cout << "有参构造的调用" << endl;
m_Capacity = capacity;
m_Size = 0;
pAddress = new T[this->m_Capacity];
}
//拷贝构造
MyArray(const MyArray& arr)
{
//cout << "拷贝构造的调用"<m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//解决浅拷贝的问题
//this->pAddress = arr.pAddress;
this->pAddress = new T[this->m_Capacity];
//将arr数组数据复制到新区,否则会造成重复释放
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
//operator=防止浅拷贝的问题 a=b=c
MyArray& operator=(const MyArray& arr)
{
//先判断原来的堆区是否有数据,如果有,先释放
if (this->pAddress)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//解决浅拷贝的问题
//this->pAddress = arr.pAddress;
this->pAddress = new T[this->m_Capacity];
//将arr数组数据复制到新区,否则会造成重复释放
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this; //为什么?
}
//尾插法
void Push_Back(const T &val) {
if (this->m_Capacity == this->m_Size) {
return;
}
else {
this->pAddress[this->m_Size] = val;
this->m_Size++;
}
}
//尾删法
void Pop_Back() {
if (this->m_Size == 0) {
return;
}
else {
this->m_Size--;
}
}
//利用下标的方式访问数组的元素
T& operator[](int index) {
return this->pAddress[index];
}
//获取数组的容量
int getCapacity() {
return this->m_Capacity;
}
//获取数组的大小
int getSize() {
return this->m_Size;
}
//析构函数
~MyArray() {
if (this->pAddress)
{
delete[] pAddress;
this->pAddress = NULL;
}
}
private:
//数组
T * pAddress; //指针指向堆区开辟的数组
//数组容量
int m_Capacity;
//数组大小
int m_Size;
};
#include
#include
using namespace std;
#include"MyArray.hpp"
class Person {
public:
Person() {};
Person(string name,int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
void printArr(MyArray arr)
{
for (int i = 0; i < arr.getSize(); i++) {
cout << arr[i].m_name<<":"<arr1(10);
Person p1("安其拉", 20);
Person p2("韩信", 57);
Person p3("牛魔王",13);
Person p4("貂蝉", 45);
Person p5("吕布", 75);
arr1.Push_Back(p1);
arr1.Push_Back(p2);
arr1.Push_Back(p3);
arr1.Push_Back(p4);
arr1.Push_Back(p5);
printArr(arr1);
}
int main()
{
test1();
system("pause");
return 0;
}