[C++ Elementary] 6. Template Elementary (Plantilla de función + Plantilla de clase)

programación genérica

import - función de intercambio genérica

Si escribes una función para el intercambio de dos números. En lenguaje C, usaríamos el siguiente método:

// 交换两个整型
void Swapi(int* p1, int* p2)
{
    
    
	int temp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
// 交换两个双精度浮点型
void Swapd(double* p1, double* p2)
{
    
    
	double temp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

Debido a que el lenguaje C no admite la sobrecarga de funciones, los nombres de función de las funciones utilizadas para intercambiar diferentes tipos de variables no pueden ser los mismos, y la forma de pasar parámetros debe ser pasar direcciones, no pasar valores.
Después de aprender la sobrecarga de funciones y la referencia de C++, usaremos el siguiente método para intercambiar dos números:

// 交换两个整型
void Swap(int& left, int& right)
{
    
    
	int temp = left;
	left = right;
	right = temp;
}
// 交换两个双精度浮点型
void Swap(double& left, double& right)
{
    
    
	double temp = left;
	left = right;
	right = temp;
}

La sobrecarga de funciones de C++ permite que las funciones utilizadas para intercambiar variables de diferentes tipos tengan el mismo nombre de función, y los parámetros se pasan por referencia para que el código parezca menos oscuro.

Sin embargo, este código todavía tiene sus defectos:

  1. Las múltiples funciones sobrecargadas son solo de diferentes tipos, y la tasa de reutilización de código es relativamente baja. Siempre que sea necesario intercambiar un nuevo tipo, se debe agregar una función sobrecargada correspondiente.
  2. La mantenibilidad del código es relativamente baja, un error puede causar que todas las sobrecargas sean un error

¿Puede decirle al compilador un modelo y dejar que el compilador use el modelo para generar automáticamente código de acuerdo con diferentes tipos ?

Al igual que jugábamos con animales de plastilina y otros modelos cuando éramos pequeños, podemos obtener modelos de animales con la misma forma pero de diferentes colores agregando materiales de diferentes colores.
inserte la descripción de la imagen aquí

Si tal molde también puede existir en C++, al llenar el molde con materiales (tipos) de diferentes colores, se pueden obtener pasteles de luna con la misma forma pero de diferentes colores (generar códigos de tipos específicos), eso reducirá en gran medida la redundancia de código. Permanecer. Coincidentemente, los antecesores ya han plantado el árbol, y aquí solo nos falta disfrutar de la sombra.

Programación genérica: Escribir código genérico que no tiene nada que ver con tipos es un medio de reutilización de código. Las plantillas son la base de la programación genérica

inserte la descripción de la imagen aquí

plantilla de función

El concepto de plantillas de funciones

Una plantilla de función representa una familia de funciones. La plantilla de función no tiene nada que ver con el tipo. Se parametriza cuando se usa, y se genera una versión de tipo específica de la función de acuerdo con el tipo del parámetro real.

formato de plantilla de función

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
    
    
	//函数体
}

Por ejemplo:

template<typename T> 
void Swap(T& left, T& right)
{
    
    
	T temp = left;
	left = right;
	right = temp;
}

Aviso:

  1. templatees una palabra clave que define una plantilla, seguida de corchetes angulares < >
  2. typenameSe usa para definir palabras clave de parámetros de plantilla y también se puede usar class(recuerde: no se puede usar struct en lugar de class)
  3. T1, T2, ..., Tn representan el nombre de la función (al igual que el nombre del parámetro de la función), que puede entenderse como el nombre de la plantilla, y puede elegir el nombre usted mismo.

Con plantillas de funciones, podemos resolver nuestro problema de intercambio anterior.
inserte la descripción de la imagen aquí
Podemos encontrar que estos diferentes tipos, hemos completado el intercambio.

El principio de las plantillas de funciones

Una plantilla de función es un modelo, no una función en sí misma. es el molde del compilador para generar funciones de un tipo concreto particular. Entonces, de hecho, la plantilla es para entregar las cosas repetitivas que deberíamos haber hecho al compilador.
inserte la descripción de la imagen aquí
En la etapa de compilación del compilador, para el uso de funciones de plantilla, el compilador necesita deducir y generar tipos de funciones correspondientes para llamar de acuerdo con el tipo de parámetros reales pasados. Por ejemplo: cuando se usa una plantilla de función con un tipo doble, el compilador determina T como un tipo doble a través de la deducción del tipo de parámetro real y luego genera un código que maneja específicamente el tipo doble, y lo mismo es cierto para el carácter tipo

Creación de instancias de plantillas de funciones

Cuando una plantilla de función se utiliza con parámetros de diferentes tipos , se denomina instanciación de la plantilla de función. La instanciación de parámetros de plantilla se divide en: instanciación implícita y instanciación explícita .

Lo anterior solo refleja el intercambio de variables del mismo tipo, si queremos intercambiar variables de diferentes tipos, se informará un error al usar la plantilla de función anterior y no se podrán satisfacer nuestras necesidades. ¿Entonces, qué debemos hacer?

1. Instanciación implícita: permita que el compilador deduzca el tipo real del parámetro de plantilla de acuerdo con el parámetro real

template<class T>
T Add(const T& left, const T& right)
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	// 自动推演实例化
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;

	return 0;
}

Esto todavía está agregando variables del mismo tipo, entonces, ¿qué pasa si está agregando diferentes tipos? ¿Por qué no se puede usar la plantilla anterior para agregar variables de diferentes tipos?

Add(a1, d1);//什么不行呢?

Nota: en las plantillas , el compilador generalmente no realiza operaciones de conversión de tipo , porque una vez que hay un problema con la conversión, el compilador debe asumir la culpa.

Porque durante la compilación, cuando el compilador ve la instanciación, necesita deducir su tipo de parámetro. T se deduce como un int por el parámetro real a1, y T se deduce como un tipo doble por el parámetro real d1, pero solo hay uno T en la lista de parámetros de plantilla El compilador no puede determinar si T debe determinarse como un tipo int o double e informa un error.

En este punto, tenemos tres formas de solucionarlo. La primera forma es convertir b en tipo int al pasar parámetros . La segunda forma es usar la creación de instancias de visualización que se menciona a continuación . La tercera forma es usar varias plantillas de parámetros.

Manejo cuando se pasan diferentes tipos de parámetros formales:

  1. Conversión forzada al pasar parámetros (los parámetros formales correspondientes necesitan modificación constante)
template<class T>
T Add(const T& left, const T& right)//const接收常性实参
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

	//对于只有一个参数模板,只能在传参时,自己去强转成一样的类型
	cout << Add((double)a1, d2) << endl;//强转,临时变量传参,具有常性
	cout << Add(a1, (int)d2) << endl;//强转,临时变量传参,具有常性

	return 0;
}

Utilice la conversión de tipo obligatoria para convertir los parámetros formales al mismo tipo durante la deducción.

  1. Instanciación explícita (conversión de tipo implícita al pasar parámetros, los parámetros formales correspondientes necesitan modificación constante)
template<class T>
T Add(const T& left, const T& right)//需要使用const接收
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

	// 显示实例化
	//指定模板参数的实际类型为double
	cout << Add<double>(a1, d2) << endl;//显式实例化,a1发生隐式类型转换
	//指定模板参数的实际类型为int
	cout << Add<int>(a1, d2) << endl;//显式实例化,d2发生隐式类型转换

	return 0;
}
  1. Usar múltiples plantillas
template<class T1, class T2>//两个参数模板,可以传不同类型的参数  //可以写typename也可以写class
T1 Add(const T1& left, const T2& right)//返回值类型只能选择其中一种
{
    
    
	return left + right;
}

int main()
{
    
    
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;

	cout << Add(a1, d2) << endl;
	cout << Add(d1, a2) << endl;

	return 0;
}

Principios de coincidencia para parámetros de plantilla

  1. Una función sin plantilla puede coexistir con una plantilla de función con el mismo nombre, y la plantilla de función también se puede instanciar como esta función sin plantilla
// 专门处理int的加法函数
int Add(int left, int right)
{
    
    
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
    
    
	return left + right;
}
void Test()
{
    
    
	Add(1, 2); // 与非模板函数匹配,调用非模板函数,编译器不需要实例化
	Add<int>(1, 2); // 调用编译器实例化的Add版本
}
  1. Para una función sin plantilla y una plantilla de función con el mismo nombre, si las demás condiciones son las mismas, la función sin plantilla se llamará primero al llamar y no se generará una instancia a partir de la plantilla. Si la plantilla puede producir una función con una mejor coincidencia, elija la plantilla
//专门用于int类型加法的非模板函数
int Add(const int& x, const int& y)
{
    
    
	return x + y;
}
//通用类型加法的函数模板
template<typename T1, typename T2>
T1 Add(const T1& x, const T2& y)
{
    
    
	return x + y;
}
int main()
{
    
    
	Add(10, 20); //与非模板函数完全匹配,不需要函数模板实例化
	Add(2.2, 2); //函数模板可以生成更加匹配的版本,编译器会根据实参生成更加匹配的Add函数
	return 0;
}
  1. Las funciones de plantilla no permiten la conversión automática de tipos, pero las funciones ordinarias pueden realizar la conversión automática de tipos.
template<typename T>
T Add(const T& x, const T& y)
{
    
    
	return x + y;
}
int main()
{
    
    
	Add(2, 2.2); //模板函数不允许自动类型转换,不能通过编译
	//因为只有一个参数模板,只能在传参时,自己去强转成一样的类型
	return 0;
}

plantilla de clase

El formato de definición de una plantilla de clase.

template<class T1, class T2, ..., class Tn>
class 类模板名
{
    
    
	// 类内成员定义
};

Por ejemplo:

template<typename T>
class Scores
{
    
    
public:
	void Print()
	{
    
    
		cout << "数学:" << _Math << endl;
		cout << "语文:" << _Chinese << endl;
		cout << "英语:" << _English << endl;
	}
private:
	T _Math;
	T _Chinese;
	T _English;
};

Nota: Si una función miembro en una plantilla de clase se define fuera de la clase, se debe agregar una lista de parámetros de plantilla.

template<class T>
class Scores
{
    
    
public:
	void Print();
private:
	T _Math;
	T _Chinese;
	T _English;
};
template<class T>
void Scores<T>::Print()
{
    
    
	cout << "数学:" << _Math << endl;
	cout << "语文:" << _Chinese << endl;
	cout << "英语:" << _English << endl;
}

Creación de instancias de plantillas de clase

La creación de instancias de plantilla de clase es diferente de la creación de instancias de plantilla de función. La creación de instancias de plantilla de clase debe seguir el nombre de la plantilla de clase con <>, y luego poner el tipo instanciado en <> (mostrar instanciación). El nombre de la plantilla de clase no es una clase real, y el el resultado de la instanciación es la clase real

La comprensión de esta oración:

//Score不是真正的类,Score<int>和Score<double>才是真正的类
Score<int> s1;//需要显示实例化
Score<double> s2;

También debemos tener en cuenta que aunque se instancian desde la misma plantilla de clase, las dos clases instanciadas no son la misma clase, porque sus tipos y tamaños son diferentes.
Por ejemplo: la clase de pila que definimos anteriormente

template<typename T>
class Stack
{
    
    
public:
	Stack(int capacity = 4)
	{
    
    
		cout << "Stack(int capacity = )" <<capacity<<endl;

		_a = (T*)malloc(sizeof(T)*capacity);
		if (_a == nullptr)
		{
    
    
			perror("malloc fail");
			exit(-1);
		}

		_top = 0;
		_capacity = capacity;
	}
	
	~Stack()
	{
    
    
		cout << "~Stack()" << endl;

		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(const T& x)
	{
    
    
		// ....
		// 扩容
		_a[_top++] = x;
	}

private:
	T* _a;
	int _top;
	int _capacity;
};


int main()
{
    
    
	// 类模板一般没有推演时机,函数模板实参传递形参,推演模板参数
	// 类模板只能显示实例化
	// 他们是同一个类模板实例化出来的
	// 但是模板参数不同,他们就是不同类型
	Stack<double> st1; // double
	st1.Push(1.1);

	Stack<int> st2; // int
	st2.Push(1);

	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/m0_58124165/article/details/127710923
Recomendado
Clasificación