首页 > 其他分享 >模板

模板

时间:2024-02-14 17:56:19浏览次数:25  
标签:name 模版 age Maker void 模板 函数

01.模版的概念(了解)

1.函数或类是通用,但是里面的数据类型的多种状态

2.模版有:函数和类

02.函数模版(重点)

1.什么是函数模版

函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板

2.怎么编写函数模版

//T代表泛型的数据类型,不是只能写T,
template<class T>//让编译器看到这句话后面紧跟着的函数里有T不要报错
void mySwap(T &a,T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}


//第二种写法 typename 
template<typename T>
void func2(T a,T b)
{

}

3.怎么使用函数模版

//T代表泛型的数据类型,不是只能写T,
template<class T>//让编译器看到这句话后面紧跟着的函数里有T不要报错
void mySwap(T &a,T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

template<class T>
void mySwap2()
{

}

//使用函数模版
void test02()
{
	int a = 10;
	int b = 20;

	//1.编译器会根据实参来自动推导数据类型
	mySwap(a,b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
    
	char c = 'c';
	//mySwap(a, c);err,数据类型要一致

	//2.显示的指定类型
	mySwap<int>(a, b);          //<>用参数列表告诉编译器我只传int类
	//mySwap<double>(a, b);     //注意:指定类型,传入时不能不一致

	mySwap<>(a,b);              //自己推导数据类型
	//mySwap2<>();              //err 调用时,必须让编译器知道泛型T具体是什么类型
    						  //只有知道变量的类型 才能分配空间大小
}

4.编译器会对函数模版和类模版进行二次编译

//T代表泛型的数据类型,不是只能写T,
template<class T>//让编译器看到这句话后面紧跟着的函数里有T不要报错
void mySwap(T &a,T &b)//第一次编译
{
	T tmp = a;
	a= b;
	b = tmp;
}

//使用函数模版
void test02()
{
	int a = 10;
	int b = 20;

	//1.编译器会根据实参来自动推导数据类型
	mySwap(a,b);
    
    //编译器在函数模版被调用时,进行第二次编译
	/*
	void mySwap(int &a,int &b)
	{
		int tmp = a;
		a= b;
		b = tmp;
	}
	*/
    
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}

5.隐式转换

template<class T>
T func(T a, T b)
{
	return a + b;
}
void test03()
{
	int a = 10;
	double b = 20.2;
    
	//如果使用参数列表指定数据类型,那么实参中可以隐式转换,
	//如果转换成功,就调用,转换不成功就报错
	cout << func<int>(10,20.2) << endl;
    //如果模板是引用类型 就不可以
    //应该进行列表声明
}

泛型排序

template<class T>
void myswap(T& a, T& b) {
	T tmp = a;
	a = b;
	b = tmp;
}

template<class T>
void sort(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;
		}
		myswap(arr[max], arr[i]);
	}
}

template<class T>
void myPrint(T arr[], int len) {
	for (int i = 0; i < len; i++)
		cout << arr[i] << endl;
}

int main() {
	char arrchar[] = "hello";
	int len = sizeof(arrchar)/sizeof(char);
	sort(arrchar, len);
	myPrint(arrchar,len);
}

03.普通函数与函数模板区别(重点)

1.普通函数可以进行隐式转换,函数模版不能直接的进行隐式转换

//普通函数
int myPlus(int a, int b)
{
	return a + b;
}

//函数模板
template<class T>
int myPlus2(T a, T b)
{
	return a + b;
}

void test()
{
	int a = 10;
	int b = 20;
	char c = 'a';
	//普通函数可以进行隐式转换
	myPlus(a, c);
    
	//函数模版不能直接的进行隐式转换 两个参数必须相同
	//myPlus2(a, c); 
	myPlus2<int>(a, c);//如果要进行隐性转换,必须加上参数列表
}

04.普通函数与函数模板的调用规则(重点)

//普通函数
void myPlus(int a, int b)
{
	cout << "普通函数" << endl;
}

//函数模板重载
template<class T>
void myPlus(T a, T b)
{
	cout << "函数模版" << endl;
}

template<class T>
void myPlus(T a, T b, T c)
{
	cout << "函数模版 T c" << endl;

}
//1.函数模版和普通函数可以重载

void test()
{
	int a = 10;
	int b = 20;
	//2.如果普通函数和函数模版都可以实现的功能,普通函数优先调用
	myPlus(a, b);

	//3.可以使用<>空参数列表强制调用函数模版
	myPlus<>(a, b);

	//4.函数模版之间也可以进行重载


	//5.如果函数模版可以产生更好的匹配,那么优先使用函数模版
	char c1 = 'a';
	char c2 = 'b';
	myPlus(c1, c2);
}

05.模版实现机制(了解)

l 编译器并不是把函数模板处理成能够处理任何类型的函数

l 函数模板通过具体类型产生不同的函数

l 编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。

06.模版的局限性及解决方法(了解)

1.模版的局限性

template<class T>
void func(T a, T b)
{
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}
void test()
{
//如果传入的是数组名,那么函数模版中比较函数名的大小就没有意义
	int arr[20];
	int arr2[10];

	func(arr, arr2);   //out  a > b
}

2.解决方法,使用函数模版具体化

//不建议具体化函数模版,因为没有通用性
class Maker
{
public:
	Maker(string name,int age)
	{
		this->age = age;
		this->name = name;
	}
public:
	string name;
	int age;
};

template<class T>
void myfunc(T &a, T &b)
{
	if (a > b)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

//不建议具体化函数模版,因为没有通用性
//具体化函数模版,注意上面的函数模版要有,才能具体化
template<>void myfunc<Maker>(Maker &a, Maker &b)  //函数名和括号之间指定类型
{
	cout << "函数模版的具体化" << endl;
	if (a.age > b.age)
	{
		cout << "a>b" << endl;
	}
	else
	{
		cout << "a<=b" << endl;
	}
}

void test02()
{
	Maker m1("aaa", 10);
	Maker m2("bbb", 20);

	myfunc(m1, m2);
}

07.类模版(重点)

1.类模版是把类中的数据类型参数化

template<class NameType,class AgeType> //自定义 以前用的T
class Maekr
{
public:
	Maekr(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
public:
	NameType name;
	AgeType age;
};

2.类模版的使用

void test()
{
	//1.类模版不会自动推导数据类型,要显示的告诉编译器是什么类型
	Maker<string,int> m("悟空",18);
	m.printMaker();

	//2.注意传入的参数,传入参数类型要程序员自己把握
	Maker<int, int> m2(18, 20);
	m2.printMaker();

	//Maker<> m3("aaa",18);err,必须通过参数列表告诉编译器是什么类型

}

3.类模版和函数模版的区别

类模版不会自动推导数据类型,要显示的告诉编译器是什么类型

函数模版可以根据实参来推导数据类型

4.类模版的默认参数(了解)

//类模版的默认类型
template<class NameType, class AgeType=int>
class Maker2
{
public:
	Maker2(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << " Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

void test02()
{
	//如果有默认类型,那么<>里可以少写类型
	Maker2<string> m("贝吉塔", 20);

	m.printMaker();
	//以传入的类型为准
	Maker2<string, double> m2("wukong", 20.22);
	m2.printMaker();
}

5.类模版的默认参数注意

//默认类型后面的泛型类型都必须有默认类型
template<class NameType, class AgeType = int,class T=int>  //是占位的就应该占位
class Maker3
{
public:
	Maker3(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << " Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

08.复数的模板类(了解)

1.目的:对象c1(3,4) ,对象c2(5,-10) ,对象c3=c1.Maker_add(c2) ,c3里的两个成员变量会等于a=3+5,b=4+(-10)并且数据类型可以不同

2.通过成员函数实现

template<class T>
class Maker {
public:
	Maker() {
		a = 0;
		b = 0;
	}
	Maker(T a,T b) {
		this->a = a;
		this->b = b;
	}
	Maker MakerAdd(Maker &c2) {
		Maker <T> c; //泛型类 接受各种类型的运算
		c.a = this->a + c2.a;
		c.b = this->b + c2.b;
		return c;
	}
	void printMaker() {
		cout << "(" << a << "," << b << ")" << endl;
	}
private:
	T a;
	T b;
};
int main() {
	Maker<int> c1(3, 4);
	Maker<int> c2(5, -10);
	Maker<int> c3;
	c3 = c1.MakerAdd(c2);
	c3.printMaker();

	Maker<double> d1(3.3, 4.4);
	Maker<double> d2(0.5888, -10.1);
	Maker<double> d3;
	d3 = d1.MakerAdd(d2);
	d3.printMaker();

}

3.通过重载运算符实现

template<class T>
class Maker
{
public:
	Maker()
	{
		a = 0;
		b = 0;

	}
	Maker(T r,T i)
	{
		a = r;
		b = i;
	}
	Maker operator+(Maker &c2)
	{
		Maker tmp(this->a + c2.a, this->b + c2.b); //利用有参构造 
		return tmp;
	}

	void printMaker()
	{
		cout << "(" << a << "," << b << ")" << endl;
	}
private:
	T a;
	T b;
};


void test02()
{
	Maker<int> c1(3, 4);
	Maker<int> c2(5, -10);
	Maker<int> c3;
	c3 = c1 + c2;

	cout << "c1+c2=";
	c3.printMaker();
}

09.类模版做函数参数(重点)

1.指定传入类型

template<class NameType, class AgeType> /
class Maekr
{
public:
	Maekr(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}
	void printmaerkr() {
		cout << name << endl << age << endl;
	}
public:
	NameType name;
	AgeType age;
};

//1.传入做指定的类型
void func(Maekr<string,int> &m) {
	m.printmaerkr();
}

int main() {
	Maekr<string, int> m1("博", 24);
	func(m1);
}

2.参数模板化

//2.参数模版化(常用)
template<class T1,class T2>
void func(Maekr<T1,T2> &m) {
	m.printmaerkr();
}

3.整个类 模板化

//3.整个类 模版化
template<class T>
void func3(T &m)
{
	m.printMaker(); //强写 会黑方法
}

10.类模版的继承(重点)

1.普通类继承类模版

//普通类继承类模版
template<class T>
class Father
{
public:
	Father()
	{
		m = 20;
	}
public:
	T m;
};

//普通类 继承 类模版 
class Son :public Father<int>              //要告诉编译器父类的泛型数据类型具体是什么类型
{
public:
	
};

2.类模版 继承 类模版

//类模版 继承 类模版
template<class T1,class T2>
class Son2 :public Father<T2>                 //要告诉编译器父类的泛型数据类型具体是什么类型
{

};

void test02(){
	Son <int,int> s; //替换T1 T2    
}

3.必须告诉编译器父类的泛型类型是什么数据类型

11.类模版的成员函数类内类外实现(重点)

1.类内实现

template<class NameType, class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	void printMaker()
	{
		cout << "Name:" << this->name << " Age:" << this->age << endl;
	}
public:
	NameType name;
	AgeType age;
};

2.类外实现,成员函数必须写成函数模版,并且写上参数列表

template<class NameType, class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age);
	void printMaker();
public:
	NameType name;
	AgeType age;
};

//类模版的成员函数类外实现
//要写成函数模版实现

template<class NameType, class AgeType>
//类名作用域后面要跟着泛型的类型名
Maker<NameType, AgeType>::Maker(NameType name, AgeType age)
{

	cout << "构造函数" << endl;
	this->name = name;
	this->age = age;
}


template<class NameType, class AgeType>
void Maker<NameType, AgeType>::printMaker()
{
	cout << "Name:" << this->name << " Age:" << this->age << endl;
}

12.类模版分文件编写问题及解决方法(重点难点)

1.调用类模版时,要进行第二次编译,要把泛型的数据类型替换为具体的类型,这时需要知道函数体,但是函数的实现在.cpp中,那么调用类模版的.cpp没有引入实现.cpp,只引入.h,所以报错

//Maker.hpp
#pragma once

//声明
template<class NameType, class AgeType>
class Maker
{
public:
	Maker(NameType name, AgeType age);
	void printMaker();
public:
	NameType name;
	AgeType age;
};

//实现
template<class NameType, class AgeType>
Maker<NameType, AgeType>::Maker(NameType name, AgeType age)
{

	cout << "构造函数" << endl;
	this->name = name;
	this->age = age;
}

template<class NameType, class AgeType>
void Maker<NameType, AgeType>::printMaker()
{
	cout << "Name:" << this->name << " Age:" << this->age << endl;
}

file.cpp
#define _CRT_SECURE_NO_WARNINGS

#include<iostream>
#include<string>
using namespace std;
#include "Maker.hpp" //hpp文件

//如果在这个地方引入的是.h文件 那么没有函数的实现会 会error
//但是问题是引入的是hpp文件 同时引入了声明和实现  链接器会对重复的成员函数进行处理,只保留一份成员函数

/*  在此文件中引入这个实现 与 hpp文件中的实现可以共存
template<class NameType, class AgeType>
Maker<NameType, AgeType>::Maker(NameType name, AgeType age)
{

	cout << "构造函数" << endl;
	this->name = name;
	this->age = age;
}

template<class NameType, class AgeType>
void Maker<NameType, AgeType>::printMaker()
{
	cout << "Name:" << this->name << " Age:" << this->age << endl;
}
	但不可以在引入hpp文件 同时引入一模一样的实现 会报error C2995 模板重定义
*/


int main() {
	Maker<string, int> m("悟空", 20);
	m.printMaker();
	cout << "s";
}

2.解决方法:把成员函数放到.h中,然后把.h改为.hpp,然后在调用成员函数的地方,引入.hpp

3.为什么.hpp中有成员的实现,然后在调用类模版的地方引入.hpp,不会报重定义

类的成员函数默认申请为内联函数,在链接时,链接器会对重复的成员函数进行处理,只保留一份成员函数,所以不会进行报错

13.类模版碰到友元(重点难点)

1.友元类内实现

template<class NameType, class AgeType>
class Maker
{
	friend void printMaker(Maker<NameType, AgeType> &p)
	{
		cout << "类内实现" << p.name << " " << p.age << endl;
	}
public:
	Maker(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}

	
private:
	NameType name;
	AgeType age;
};

void test01()
{
	Maker<string, int> m("悟空", 18);

	printMaker(m);
}

2.友元类外实现倒着分析

//1.声类类模版
template<class NameType, class AgeType>
class Maker2;

//告诉编译器下面有printMaker2的实现 
//2.声明函数模版
template<class NameType, class AgeType>
void printMaker2(Maker2<NameType, AgeType> &p);  

template<class NameType, class AgeType>
class Maker2
{
	//3.在函数名和()之间加上空成员参数列表<>。
	friend void printMaker2<>(Maker2<NameType, AgeType> &p);
	//编译器不知道printMaker2下面有没有实现,需要知道函数的结构
public:
	Maker2(NameType name, AgeType age)
	{
		this->name = name;
		this->age = age;
	}


private:
	NameType name;
	AgeType age;
};

//4.友元在类外实现要写成函数模版
template<class NameType, class AgeType> //函数模板声明
void printMaker2(Maker2<NameType, AgeType> &p) //当形参
{
	cout << "类外实现的友元函数 " << p.name << " " << p.age << endl;  //访问私有成员需要 变成友元
}

void test02()
{
	Maker2<string, int> m("贝吉塔", 18);

	printMaker2(m);
}

14.类模版实现数组

MyArray.hpp
#pragma once

template<class T>
class MyArray
{
public:
	MyArray(int capacity)
	{
		this->mCapacity = capacity;
		this->mSize = 0;
		//T如果是Maker,这里要调用什么构造函数,要调用无参构造
		p = new T[this->mCapacity];

	}

	//拷贝构造
	MyArray(const MyArray &arr)
	{
		this->mCapacity = arr.mCapacity;
		this->mSize = arr.mSize;
		p = new T[arr.mCapacity];

		for (int i = 0; i < this->mSize; i++)
		{
			p[i] = arr.p[i];
		}
	}
	//赋值函数
	MyArray &operator=(const MyArray &arr)
	{
		if (this->p != NULL)
		{
			delete[] this->p;
			this->p = NULL;
		}

		p = new T[arr.mCapacity];
		this->mSize = arr.mSize;
		this->mCapacity = arr.mCapacity;
		for (int i = 0; i < this->mSize; i++)
		{
			p[i] = arr.p[i];

		}

		return *this;
	}

	//重载[]
	T &operator[](int index)
	{
		return this->p[index];
	}
	//尾插
	void Push_Back(const T &val)
	{
		if (this->mSize == this->mCapacity)
		{
			return;
		}

		this->p[this->mSize] = val;
		this->mSize++;
	}

	//尾删
	void Pop_Back()
	{
		if (this->mSize == 0)
		{
			return;
		}

		this->mSize--;
	}

	~MyArray()
	{
		if (this->p != NULL)
		{
			delete[] p;
			p = NULL;
		}
	}

	int getSize()
	{
		return this->mSize;
	}
private:
	T *p;
	int mCapacity;
	int mSize;
};

//类模版实现数组.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include"MyArray.hpp"
#include<string>
class Maker
{
public:
	Maker(){} //因为创建类数组 要调用无参构造
	Maker(string name, int age)
	{
		this->name = name;
		this->age = age;
	}
public:
	string name;
	int age;
};

void printMaker(MyArray<Maker> &arr) //模板类实例化当形参
{
	for (int i = 0; i < arr.getSize(); i++)
	{
		cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << endl;
	}
}
void test()
{
	MyArray<Maker> myarr(4);
	Maker m1("悟空", 18);
	Maker m2("贝吉塔", 30);
	Maker m3("短笛", 200);
	Maker m4("小林", 19);
	myarr.Push_Back(m1);
	myarr.Push_Back(m2);
	myarr.Push_Back(m3);
	myarr.Push_Back(m4);

	printMaker(myarr);

	MyArray<int> myint(10);
	for (int i = 0; i < 10; i++)
	{
		myint.Push_Back(i + 1);
	}

	for (int i = 0; i < 10; i++)
	{
		cout << myint[i] <<" ";
	}
	cout << endl;
}

int main()
{
	test();

	system("pause");
	return EXIT_SUCCESS;
}


标签:name,模版,age,Maker,void,模板,函数
From: https://www.cnblogs.com/wbcde116/p/18015369

相关文章

  • P3367 【模板】并查集
    原题链接并查集模板练手。递归版本#include<bits/stdc++.h>usingnamespacestd;constintN=1e4+5;intfather[N];intfind(intmid){if(father[mid]!=mid){father[mid]=find(father[mid]);}returnfather[mid];}voidunion_fa(intx,inty){......
  • P4512 【模板】多项式除法
    为什么不能直接用\(F(x)\timesG(x)^{-1}\)做?\(G(x)^{-1}\)是模\(x^{m+1}\)意义下的,\(n-m>m\)时得到的\(Q(x)\)就是错的。记\(F'(x)\)为\(F(x)\)系数翻转后的多项式,即若\(F(x)=\sum\limits_{i=0}^nf_ix^i\),则\(F'(x)=\sum\limits_{i=0}^nf_{n......
  • P3811 【模板】模意义下的乘法逆元
    原题链接题解由于时间限制过于严苛,遂采用线性递推方式\(p=k·i+b\),\((1\leqslantb<r<p)\)\(k·i+b=0\)\((mod\p)\)同时乘上\(i^{-1}\b^{-1}\)\(k·b^{-1}+i^{-1}=0\(mod\p)\)\(i^{-1}=-k·b^{-1}\(mod\p)\)\(i^{-1}=(-[\frac{p}{i}]+p)+(p\mod\i)^{-1}......
  • 各大排序的模板
    1.冒泡排序 1for(i=n;i>=1;--i)2{3for(j=1;j<=i;++j)4{5if(a[j]>a[j+1])6{7swap(a[j],a[j+1]);8}9}10}   2.快速排序1.懒人函数 1sort(a+1,a+n+1);   2.正常的1vo......
  • grafana模板参考
     空的,把面板都删除了{"__inputs":[{"name":"DS_PROMETHEUS","label":"Prometheus","description":"","type":"datasource","pl......
  • Link Cut Tree模板(从别人那里拿的)
    可以通过这道题#include<bits/stdc++.h>#defineRregisterint#defineIinlinevoid#defineGif(++ip==ie)if(fread(ip=buf,1,SZ,stdin))#definelcc[x][0]#definercc[x][1]usingnamespacestd;constintSZ=1<<19,N=3e5+9;charbuf[SZ],*ie=buf+SZ,*ip=......
  • BootstrapBlazor 模板适配移动设备使用笔记
    项目模板BootstrapBlazorApp模板为了方便大家利用这套组件快速搭建项目,作者制作了项目模板(ProjectTemplates),使用dotnetnew命令行模式,使用步骤如下:安装项目模板dotnetnewinstallBootstrap.Blazor.Templates::8.0.1创建工程dotnetnewbbapp官网教程https:......
  • Liquid模板引擎简单使用
    最近在写一个配置表导出工具,自动生成代码那边会用到模板引擎,所以就熟悉了下Liquid的使用。 需要用到一个DotLiquid的库usingDotLiquid;varlqTemplate=Template.Parse(templateContent);vartemplateHash=newHash();//todo逻辑部分using(varsw=newStrea......
  • 设计模式-模板方法模式(Template Method Pattern)
    #模板方法模式(TemplateMethodPattern)-记忆关键字:模板方法-定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤-类型:行为型-![UML类图](./design-pattern.png)##1.涉及的角色1)Abstr......
  • P4721 【模板】分治 FFT
    最具经济性的写法:\(\mathcalO(n^2)\)暴力拿下\(80\)分,遂跑路。一题多解了,分两部分:分治和多项式求逆。分治考虑cdq分治,每次把\(f_{l\dotsmid}\)和\(g_{1\dotsn-1}\)卷起来,贡献直接加到\(f_{mid+1\dotsr}\)里,要注意一下顺序,先递归左区间,再算当前区间,最......