3.1 Plantillas de C++

1. Plantilla

La plantilla es para establecer un molde general, lo que mejora en gran medida la reutilización.Se proporcionan dos mecanismos de plantilla en C++: plantilla de función y plantilla de clase

1.1 Plantillas de funciones

Una plantilla de función es para crear una función general, cuyo tipo de valor de retorno de función y tipo de parámetro formal no se especifican específicamente, pero están representados por un tipo virtual.

1.1.1 Sintaxis de plantillas de funciones

// 函数模板利用关键字template
template<Typename T>
// 函数声明或定义

// template -- 声明创建模板
// Typename -- 表明其后的符号是一种数据类型,可以用class代替
// T -- 通用的数据类型,名称可以替换,通常为大写字母

ejemplo de código

#include<iostream>
using namespace std;

template<typename T>
void mySwap(T& a, T& b)
{
    
    
	T temp = a;
	a = b;
	b = temp;
}

void test()
{
    
    
	int a = 10;
	int b = 20;
	mySwap<int>(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b  << endl;
}

int main()
{
    
    
	test();
	system("pause");
	return 0;
}

1.1.2 Notas sobre las plantillas de funciones

Hay dos formas de usar plantillas de funciones: deducción automática de tipos y asignación explícita de tipos

  • Al usar plantillas, se debe determinar el tipo de datos general T y se puede deducir un tipo consistente
// 利用模板提供通用的交换函数
template<class T>
void mySwap(T& a, T& b)
{
    
    
	T temp = a;
	a = b;
	b = temp;
}

// 1. 自动类型推导.必须推导出一致的数据类型T,才可以使用
void test01()
{
    
    
	int a = 10;
	int b = 20;
	char c = 'c';
	mySwap(a, b); // 正确, 可以推导出一致的T
	// mySwap(a, c); // 错误, 推导不出一致的T类型
}
// 2. 模板必须要确定出T的数据类型才可以使用
template<class T>
void func()
{
    
    
	cout << "func 调用" << endl;
}
void test02()
{
    
    
	// func(); // 错误, 模板不能独立使用,必须确定出T的类型
	func<int>(); // 利用显示指定类型的方式,给T一个类型,才可以使用该模板
}

1.1.3 Caso de plantilla de función

Descripción del caso:

  • Use la plantilla de función para encapsular una función de clasificación, que puede clasificar matrices de diferentes tipos de datos
  • Las reglas de clasificación son de mayor a menor, y el algoritmo de clasificación es de selección.
  • Prueba con char array y int array respectivamente
// 交换的函数模板
template<typename T>
void mySwap(T& a, T& b)
{
    
    
	T temp = a; 
	a = b;
	b = temp;
}

template<class T>
// 利用选择排序,进行数组从大到小排序
void mySort(T arr[], int len)
{
    
    
	for(int i = 0;, i < len; i++)
	{
    
    
		max = i;
		for(int j = i+1; j < len; j++)
		{
    
    
			if(arr[max] < arr[j])
			{
    
    
				max = j;
			}
		}
		if(max != i)
		{
    
    
			mySort(arr[i], arr[max]);  // 如果最大数的下标不是i,交换两者
		}
	}
}

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

void test01()
{
    
    
	// 测试char数组
	char charArr[] = "defgabc";
	int num = sizeof(charArr)/sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}

void test02()
{
    
    
	// 测试int数组
	int intArr = {
    
    7, 5, 8, 1, 3, 4,, 6, 2}
	int num = sizeof(charArr)/sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

void main()
{
    
    
	system("pause");
	return 0;
}
// h g f e d c b a
// 8 7 6 5 4 3 2 1

1.1.4 La diferencia entre funciones ordinarias y plantillas de funciones

La diferencia entre funciones ordinarias y plantillas de funciones:

  1. La conversión de tipo automática (conversión de tipo implícita) puede ocurrir cuando llamadas a funciones ordinarias
  2. Cuando se llama a la plantilla de función, si se utiliza la deducción automática de tipo, no se producirá ninguna conversión de tipo implícita (como cadena a código ASII).
  3. La conversión de tipo implícito puede ocurrir si se usa el tipo explícito
#include<iostream>
using namespace std;

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

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

// 使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
void test_01()
{
    
    
	int a = 10;
	int b = 20;
	char c = 'c';

	cout << myAdd01(a, c) << endl;       // 正确, 将char类型'c'隐式转换为int类型 ASII码  c -- 99
	// cout << myAdd02(a, c) << endl;    // 报错,使用自动类型推导时,不会发生隐式类型转换 
	cout << myAdd02<int>(a, c) << endl; // 正确,如果用显示指定类型,可以发生隐式类型转换
}

int main() 
{
    
    
	test_01();
	system("pause");
	return 0;
}	
// 109
// 109

1.1.5 Reglas de llamada para funciones ordinarias y plantillas de funciones

Las reglas de llamada son las siguientes

  1. Si se pueden implementar tanto la plantilla de función como la función normal, primero se llama a la función normal
  2. Se puede forzar la invocación de plantillas de funciones con una lista de parámetros de plantilla vacía
  3. Las plantillas de funciones también se pueden sobrecargar
  4. Si la plantilla de función puede producir una mejor coincidencia, se llama primero a la plantilla de función

ejemplo de código

#include<iostream>;
using namespace std;

// 普通函数与函数模板的调用规则
void myPrint(int a, int b)
{
    
    
	cout << "调用普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
    
    
	cout << "调用模板" << endl;
}

template<class T>
void myPrint(T a, T b, T c)
{
    
    
	cout << "调用重载的模板" << endl;
}

void test_01() 
{
    
    
	// 1. 如果函数模板和普通函数都能实现,优先调用普通函数
	int a = 10;
	int b = 10;
	myPrint(a, b); // 调用普通函数

	// 2. 可以通过空模板参数列表来强制调用函数模板
	myPrint<>(a, b);

	// 3. 函数模板也可以发生重载
	int c = 30;
	myPrint(a, b, c);

	// 4. 如果函数模板可以产生更好的匹配,优先调用函数模板
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);
}

int main() 
{
    
     
	test_01();
	system("pause");
	return 0;
}
// 调用普通函数
// 调用模板
// 调用重载的模板
// 调用模板

1.2 Plantillas de clase

La función de las plantillas de clase: para crear una clase general, no se pueden especificar los tipos de datos de miembros de la clase y se puede usar un tipo virtual para representar

1.2.1 Sintaxis de plantillas de clase

// 类模板和函数模板语法极为相似,在声明模板template后面加类,此类称为类模板
template<typename T>
// 类

ejemplo de código

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

// 类模板
template<class NameType, class AgeType>
class Person
{
    
    
public:
	Person(NameType name, AgeType age)
	{
    
    
		this->mName = name;
		this->mAge = age;
	}
	void showPerson() 
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

void test_01() 
{
    
    
	// 指定NameType为string 类型 AgeType 为int 类型
	Person<string, int>P1("孙悟空", 999);
	P1.showPerson();
}

int main() 
{
    
    
	test_01();
	system("pause");
	return 0;
}
// name: 孙悟空 age: 999

1.2.2 La diferencia entre plantillas de clase y plantillas de función

Hay dos diferencias principales entre las plantillas de clase y las plantillas de función:

  1. Cómo utilizar la deducción automática de tipo de plantillas de clase en Estados Unidos y Europa
  2. Las plantillas de clase pueden tener parámetros predeterminados en la lista de parámetros de plantilla

Ejemplo de código:

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

// 类模板和函数模板的区别主要有两点
// 1. 类模板没有自动类型推导的使用方式
// 2. 类模板在模板参数列表众可以有默认参数

// 类模板
template<class NameType, class AgeType=int>
class Person
{
    
    
public:
	Person(NameType name, AgeType age)
	{
    
    
		this->mName = name;
		this->mAge = age;
	}
	void showPerson() 
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

// 1. 类模板没有自动类型推导的使用方式
void test_01() 
{
    
    
	// Person132 p('孙悟空', 1000); // 错误,类模板使用时,不可以用自动类型推导
	Person132<string, int> p("孙悟空", 1000); // 必须使用显示指定类型的方式,使用类模板
	p.showPerson();
}

// 2. 类模板在模板参数列表中可以有默认参数
void test_02() 
{
    
    
	Person132<string> p2("猪八戒", 999); // 可以制定默认参数
	p2.showPerson();
}
int main()
{
    
    
	test_01();
	test02();
	system("pause");
	return 0;
}
// name: 孙悟空 age: 1000
// name: 猪八戒 age: 999

1.2.3 Cuándo crear funciones miembro en plantillas de clase

Hay una diferencia en el tiempo de creación de funciones miembro en plantillas de clase y funciones miembro en funciones ordinarias:

  1. Las funciones miembro en clases ordinarias se pueden crear desde el principio
  2. Las funciones miembro en las plantillas de clase se crean cuando se las llama

Ejemplo de código:

#include<iostream>
using namespace std;

class Person_01 
{
    
    
public:
	void showPerson_01() 
	{
    
    
		cout << "Person1 show" << endl;
	}
};

class Person_02
{
    
    
public:
	void showPerson_02()
	{
    
    
		cout << "Person2 show" << endl;
	}
};

template<class T>
class MyClass
{
    
    
public:
	T obj;
	// 类模板中的成员函数,并不是一开始就创建,而是在模板调用时在生成
	void fun1() {
    
     obj.showPerson_01(); }
	void fun2() {
    
     obj.showPerson_02(); }
};

void test_01()
{
    
    
	MyClass<Person_01> m;
	m.fun1();
	// m.fun2();  // 编译会出错,说明函数调用才会创建成员函数
}

int main() 
{
    
    
	test_01();
	system("pause");
	return 0;
}
// Person1 show

1.2.4 Objeto de plantilla de clase como parámetro de función

Hay tres formas de pasar parámetros a funciones para objetos instanciados por plantillas de clase

  1. Especifique el tipo entrante: muestre directamente el tipo de datos del objeto
  2. Plantillas de parámetros: convierta los parámetros del objeto en plantillas para pasar
  3. Toda la clase con plantilla: plantillas de este tipo de objeto para pasar

Ejemplo de código:

#include<iostream>
using namespace std;

// 类模板对象做函数参数,共有三种传入方式
// 1. 指定传入的类型  --- 直接显示对象的数据类型
// 2. 参数模板化      --- 将对象中参数变为模板进行传递
// 3. 整个类模板化    --- 将这个对象类型模板化进行传递

// 类模板
template<class NameType, class AgeType = int>
class Person
{
    
    
public:
	Person(NameType name , AgeType age) 
	{
    
    
		this->mName = name;
		this->mAge = age;
	}
	void showPerson() 
	{
    
    
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};


// 1. 传入指定的类型
void printPerson1(Person<string, int> &p) 
{
    
    
	p.showPerson();
}
void test_01() 
{
    
    
	Person<string, int> p("孙悟空", 100);
	printPerson1(p);
}

// 2. 参数模板化
template<class T1, class T2>
void printPerson2(Person<T1 , T2> &p) 
{
    
    
	p.showPerson();
	cout << "T1的类型为: " << typeid(T1).name() << endl;
	cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test_02() 
{
    
    
	Person<string, int> p("孙悟空", 100);
	printPerson2(p);
}

// 3. 整个类模板化
template<class T>
void printPerson3(T& p) 
{
    
    
	cout << "T的类型为: " << typeid(T).name() << endl;
	p.showPerson();
}
void test_03()
{
    
    
	Person<string, int> p("唐僧", 100);
	printPerson3(p);
}

int main()
{
    
    
	test_01();
	test_02();
	test_03();
	system("pause");
	return 0;
}
// name: 孙悟空 age: 100
// name: 孙悟空 age: 100
// T1的类型为: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
// T2的类型为: int
// T的类型为: class Person<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>
// name: 唐僧 age: 100

1.2.5 Plantillas de clase y herencia

Cuando las plantillas de clase encuentran herencia, debe prestar atención a los siguientes puntos:

  1. Si la clase principal es una plantilla de clase, la subclase debe especificar el tipo de datos de T en la clase principal
  2. Si no se especifica, el compilador no puede asignar memoria a la subclase
  3. Si desea especificar de manera flexible el tipo de T en la clase principal, la subclase también debe convertirse en una plantilla de clase

Ejemplo de código:

#include<iostream>
using namespace std;

template<class T>
class Base
{
    
    
	T m;
};

// class Son :public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> // 必须指定一个类型
{
    
    
};

void test_01()
{
    
    
	Son c;
}

// 类模板继承类模板,可以用T2指定父类中T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
    
    
public:
	Son2()
	{
    
    
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};
void test_02()
{
    
    
	Son2<int, char> c;
}

int main()
{
    
    
	test_01();
	test_02();
	system("pause");
	return 0;
}
// int
// char

1.2.6 Implementación fuera de clase de funciones miembro de plantilla de clase

Cuando una función miembro en una plantilla de clase se implementa fuera de la clase, se debe agregar una lista de parámetros de plantilla

Ejemplo de código:

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

// 类模板中成员函数类外实现
template<class T1, class T2>
class Person
{
    
    
public:
	// 成员函数类内声明
	Person(T1 name, T2 age);
	void showPerson();
public:
	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>::showPerson()
{
    
    
	cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

void test_01() 
{
    
    
	Person<string, int>p("Tom", 20);
	p.showPerson();
}

int main()
{
    
    
	test_01();
	system("pause");
	return 0;
}
// name: Tom age: 20

1.2.7 Compilación de archivos de plantilla de clase

Problemas existentes: El tiempo de creación de funciones miembro en las plantillas de clase se encuentra en la fase de llamada, lo que conduce a fallas en el enlace al escribir archivos separados.Solución: Opción 1:
Incluya
directamente el archivo fuente
.cpp el mismo archivo, y cambie el nombre a .hpp, hpp es un nombre acordado, no obligatorio (práctica convencional)

Ejemplo de código, código en person.hpp:

#pragma once
#include<iostream>
using namespace std;
#include<string>

template<class T1, class T2>
class Person 
{
    
    
public:
	Person(T1 name, T2 age);
	void showPerson();
public:
	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>::showPerson()
{
    
    
	cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
}

El código de .cpp en la escritura del archivo de plantilla de clase

#include<iostream>
using namespace std;
// 方案一 包含cpp源文件
// include "person.cpp" // 在person.h中声明, person.cpp中实现
// 方案二 将声明和实现写到一起,文件后缀名改为.hpp
#include "person.hpp"
void test_01() 
{
    
    
	Person<string, int> p("Tom", 10);
	p.showPerson();
}
int main()
{
    
    
	test_01();
	system("pause");
	return 0;
}
// 姓名: Tom 年龄: 10

1.2.8 Plantillas de clase y amigos

La función global se implementa en la clase: simplemente declare el amigo directamente en la clase.
La función global se implementa fuera de la clase: el compilador necesita saber la existencia de la función global por adelantado.

Ejemplo de código:

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

// 全局函数类内实现 - 直接在类内声明友元即可
// 全局函数类外实现 - 需要提前让编译器知道全局函数的存在
template<class T1, class T2> class Person138;

template<class T1, class T2>
void printPerson2(Person<T1, T2> &p)
{
    
    
	cout << "类外实现 --- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
    
    
	// 1. 全局函数配合友元  类内实现
	friend void printPerson(Person<T1, T2> &p)
	{
    
    
		cout << " 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
	}
	// 2. 全局函数配合友元 类外实现
	// 需要加空模板参数列表
	// 类外实现需要让编译器提前知道这个函数的存在
	friend void printPerson2<>(Person<T1, T2>& p);

public:
	Person(T1 name, T2 age)
	{
    
    
		this->m_Name = name;
		this->m_Age = age;
	};
public:
	T1 m_Name;
	T2 m_Age;
};

// 1. 全局函数在类内实现
void test_01()
{
    
    
	Person<string, int> p("Tom", 20);
	printPerson(p);
}

// 2. 全局函数在类外实现
void test_02()
{
    
    
	Person<string, int> p("Jerry", 30);
	printPerson2(p);
}

int  main()
{
    
    
	test_01();
	test_02();
	system("pause");
	return 0;
}
//  姓名: Tom 年龄:20
//  类外实现 --- 姓名: Jerry 年龄:30

mapa

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/yewumeng123/article/details/131037359
Recomendado
Clasificación