C++中的模板

本文探讨了函数模板如何创建通用函数,类模板如何定义数据类型参数化,包括类型推导、默认参数、成员函数实现和友元处理。通过实例展示了不同类型数据的交换、排序和类模板的使用方法,以及类模板继承和友元的注意事项。
摘要由CSDN通过智能技术生成

模版就是建立一个通用的模具,大大提高复用性。

1、函数模板

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

目的:将类型参数化。

语法:

template<typename T> //函数的声明或定义

template:声明创建模板

typename:表明其后面的是一种数据类型,可以用class替代

T:通用的数据类型,名称可以替换,通常为大写字母

普通函数调用时可以发生自动类型转换(隐式类型转换)。

模板并不是万能的。

例一:交换两个数据,根据类型分别写对应函数。

#include<iostream>
using namespace std;
//交换两个整形数据
void swapInt(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}
//交换两个浮点型数据
void swapDouble(double& a, double& b)
{
	double temp = a;
	a = b;
	b = temp;
}
void test01()
{
	int a = 10;
	int b = 20;
	swapInt(a, b);
	cout << a << endl;
	cout << b << endl;
	double c = 1.1;
	double d = 2.2;
	swapDouble(c,d);
	cout << c << endl;
	cout << d << endl;
}
int main()
{
	test01();
}

例二:使用函数模板完成任意类型数据的相加

#include<iostream>
using namespace std;
//交换两个T型数据
template<typename T> 
void Myswap(T& a, T&b)
{
	T temp = a;
	a = b;
	b = temp;
}
void test01()
{
	int a = 10;
	int b = 20;
	//1、自动类型推导
	Myswap(a, b);
	//2、显示指定类型
	//Myswap<int>(a,b);
	cout << a << endl;
	cout << b << endl;
}
int main()
{
	test01();
}

注意:模板必须确定T的数据类型,才可以使用。且数据类型必须一致。例如上例中将整形和字符型进行交换,数据类型不同将会报错。

例:利用函数模板对不同数据类型的数组进行从大到小的选择排序。

#include<iostream>
using namespace std;
//交换两个T型数据

template<typename T>
void mySwap(T&a,T&b)
{
	T temp = a;
	a = b;
	b = temp;
}
template<typename T>
void printArr(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

template<typename T> 
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[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i)
		{
			mySwap(arr[max], arr[i]);
		}
	}
}
void test01()
{
	char charArr[] = "badcfe"; //测试char数组
	int num = sizeof(charArr) / sizeof(char);
	Mysort(charArr, num);
	printArr(charArr, num);
}
int main()
{
	test01();
}

2、类模板

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

语法:

Template<typename T>

template<typename T> //声明创建模板

template:声明创建模板

typename:表明其后面的是一种数据类型,可以用class替代

T:通用的数据类型,名称可以替换,通常为大写字母

类模板的传入参数也需要在确定对象时也需要初始化。

#include<iostream>
#include"string"
using namespace std;


template<class NameType, class AgeType>
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "Name: " << this->m_Name << endl;
		cout << "Age: " << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};

void test01()
{
	Person<string, int> p1("wxq", 29);
	//<>模版的参数列表
	p1.showPerson();
}
int main()
{
	test01();
}

2.1、类模板和函数模板的区别

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

#include<iostream>
#include"string"
using namespace std;


template<class NameType, class AgeType>
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "Name: " << this->m_Name << endl;
		cout << "Age: " << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};
//类模板没有自动推导类型
void test01()
{
	//Person p1("wxq", 29); 
	//错误,无法用自动类型推导
	
	Person<string, int> p2("wxq", 29);
	//只能用显示指定类型
	p2.showPerson();
}
int main()
{
	test01();
}

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

#include<iostream>
#include"string"
using namespace std;


template<class NameType, class AgeType=int>
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "Name: " << this->m_Name << endl;
		cout << "Age: " << this->m_Age << endl;
	}
	NameType m_Name;
	AgeType m_Age;
};
//类模板在模板参数列表中可以有默认参数
void test01()
{
	Person<string> p2("wxq", 29);
	//只能用显示指定类型
	p2.showPerson();
}
int main()
{
	test01();
}

类模板中的成员函数

类模板中的成员函数和普通类中的成员函数的创建时机是不同的:普通类的成员函数一开始就可以创建成员函数;类模板中的成员函数在调用时创建。

#include<iostream>
#include"string"
using namespace std;
class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 shows!" << endl;
	}
};
class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 shows!" << endl;
	}
};
template<class T>
class Person
{
public:
	T obj;
	void func1()
	{
		obj.showPerson1();
	}
	void func2()
	{
		obj.showPerson2();
	}
};

void test01()
{
	Person<Person1> m;
	m.func1();
	//m.func2();  //报错,因为m中无showPerson2函数
}
int main()
{
	test01();
}

类模板对象做函数参数

类模板实例出的对象作为函数的实参,一共有三种传入方式:

1、指定类型的传入:直接显示对象的数据类型(主要使用)

2、参数模板化:将对象中的参数变为模板进行传递(类模板配合函数模版)

3、整个类模板化:将这个对象类型模板化进行传递

#include<iostream>
#include"string"
using namespace std;
template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	void showPerson()
	{
		cout << "name is " <<m_Name<< endl;
		cout << "age is " << m_Age<<endl;
	}
	T1 m_Name;
	T2 m_Age;
};
//1、指定传入类型
void printPerson1(Person<string,int>& p)
{
	p.showPerson();
}
void test01()
{
	Person<string, int> p("Wang", 100);
	printPerson1(p);
}
//2、将参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
	p.showPerson();
}
void test02()
{
	Person<string, int> p("Li", 90);
	printPerson2(p);
}
//3、整个类模板化
template<class T>
void printPerson3(T& p)
{
	p.showPerson();
}
void test03()
{
	Person<string, int> p("Zhang", 80);
	printPerson3(p);
}
int main()
{
	test01();
	test02();
	test03();
}

类模板与继承

需要注意几个点:

1、当子类继承的父类是一个类模板时,子类在声明时需要指定父类中T的类型;如果不指定,编译器则无法给子类分配内存。

2、如果想灵活指定父类中的T的类型,子类也需要变为类模板。

如果父类是类模板,子类在继承时需要指定父类中的数据类型。

#include<iostream>
#include"string"
using namespace std;
template<class T>
class Base
{
	T m;
};
//class  Son :public Base  //错误,必须知道父类的T的数据类型,才能继承给子类
//class  Son :public Base<int> //正确,但是子类只能为int
template<class T1, class T2>
class  Son :public Base<T2>
{
	T1 obj;
};
void test01()
{
	Son<int,char> s1;  //int传给T1,char传给T2,父类中的T也为char
}
int main()
{
	test01();
}

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

#include<iostream>
#include"string"
using namespace std;
template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);
	void show();
	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>::show()
{
	cout << "Name is " << this->m_Name << endl;
	cout << "Age is " << this->m_Age << endl;
}
void test01()
{
	Person<string, int> p("Wang", 100);
	p.show();
}
int main()
{
	test01();
}

类模板分文件编写

类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。

方法:

1、直接包含CPP源文件

2、将声明个实现写在同一个文件中,并更改后缀名为.hpp。hpp是一个约定的概念,表示的就是类模板的分文件编写。(用的多)

方法1:

Person.h

Person.cpp

Main.cpp

#pragma once

#include<iostream>

#include"string"

using namespace std;

template<class T1, class T2>

class Person

{

public:

         Person(T1 name, T2 age);

         void show();

         T1 m_Name;

         T2 m_Age;

};

#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 Person<T1, T2>::show()

{

         cout << "Name is " << this->m_Name << endl;

         cout << "Age is " << this->m_Age << endl;

}

#include"Person.cpp"  //包含的是cpp,不是h

//第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件

void test01()

{

         Person<string, int> p("Wang", 100);

         p.show();

}

int main()

{

         test01();

}

方法2

Person.hpp

Main.cpp

#pragma once

#include<iostream>

#include"string"

using namespace std;

template<class T1, class T2>

class Person

{

public:

         Person(T1 name, T2 age);

         void show();

         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>::show()

{

         cout << "Name is " << this->m_Name << endl;

         cout << "Age is " << this->m_Age << endl;

}

#include"Person.hpp"  //包含的是cpp,不是h

//第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件

void test01()

{

         Person<string, int> p("Wang", 100);

         p.show();

}

int main()

{

         test01();

}

类模板与友元

主要包括友元函数的类内实现和类外实现。类内直接声明友元即可,类外实现则需要提前让编译器指导全局函数的存在。

case 1:友元函数在类内实现

#include<iostream>
#include"string"
using namespace std;
template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	friend void show(Person<T1, T2> p)
	{
		cout << "Name is " <<p.m_Name << endl;
		cout << "Age is " << p.m_Age << endl;
	}
private:
	T1 m_Name;
	T2 m_Age;
};
//1、全局函数在类内实现
void test01()
{
	Person<string, int> p("Wang", 100);
	show(p);
}
int main()
{
	test01();
}

case 2:友元函数在类外实现

#include<iostream>
#include"string"
using namespace std;
//提前让编译器知道person类的存在
template<class T1, class T2>
class Person;

//2、全局函数类外实现,先让编译器知道有个类外函数的存在
template<class T1, class T2>
void show(Person<T1, T2> p)
{
	cout << "Name is " << p.m_Name << endl;
	cout << "Age is " << p.m_Age << endl;
}

template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	//加空模板的参数列表 <>
	//如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
	friend void show<>(Person<T1, T2> p);
private:
	T1 m_Name;
	T2 m_Age;
};

void test01()
{
	Person<string, int> p("Wang", 100);
	show(p);
}
int main()
{
	test01();
}

类外实现比较复杂,首先需要让编译器知道有一个全局函数的存在,由于用到了person,要先让编译器知道person类存在,person类又是一个类模板,因此还得声明类模板。

如无特殊需求,尽量类内实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值