Syntax of template class for C + + basic learning

Posted by rayden on Sat, 22 Jan 2022 15:18:39 +0100

1. Concept of template

Templates are general molds, which can greatly improve the reusability, such as templates in life and templates for one inch photos:

PPT template:

It can also be concluded from the above that the characteristics of the template are:

  • The template can't be used directly. It's just a framework
  • The universality of templates is not everything

2. Function template

  • Another programming idea of C + + is called generic programming, and the main technology is template
  • C + + provides two template mechanisms: function template and class template

2.1 function template syntax

Function template function:
Establish a general function whose function return value type and formal parameter type can be represented by a virtual type without specific formulation.
Syntax:

template<typename T>
Function declaration or definition

Explanation:
Template - declare the creation of a template
typename - indicates that the symbol after it is a data type, which can be replaced by class
T-A common data type whose name can be replaced, usually in uppercase letters
Code example:

//Commutative integer function
void swapInt(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

//Swap floating point functions
void swapDouble(double& a, double& b) {
	double temp = a;
	a = b;
	b = temp;
}

//Using templates to provide general exchange functions
template<typename T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 10;
	int b = 20;
	
	//swapInt(a, b);

	//Exchange using template
	//1. Automatic type derivation
	mySwap(a, b);

	//2. Displays the specified type
	mySwap<int>(a, b);

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

}

int main() {

	test01();

	system("pause");

	return 0;
}

2.2. Precautions for function template

matters needing attention:

  • For automatic type derivation, a consistent data type T must be derived before it can be used
  • The template can be used only after the data type of T is determined
    Code example:
//Using templates to provide general exchange functions
template<class T>
void mySwap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}


// 1. For automatic type derivation, a consistent data type T must be derived before it can be used
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';

	mySwap(a, b); // Correct, a consistent T can be derived
	//mySwap(a, c); //  Error, unable to derive a consistent T type
}


// 2. The template can be used only after the data type of T is determined
template<class T>
void func()
{
	cout << "func call" << endl;
}

void test02()
{
	//func(); // Error, the template cannot be used independently. The type of T must be determined
	func<int>(); //The template can be used only after T is given a type by displaying the specified type
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

Summary:

  • When using the template, the common data type T must be determined and a consistent type can be derived

2.3 function template cases

  • The function template is used to encapsulate a sorting function, which can sort arrays of different data types
  • The sorting rules are from large to small, and the sorting algorithm is selective sorting
  • Test with char array and int array respectively
    Example:
//Exchanged function templates
template<typename T>
void mySwap(T &a, T&b)
{
	T temp = a;
	a = b;
	b = temp;
}


template<class T> // You can also replace it with typename
//Select sorting is used to sort the array from large to small
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i; //Subscript of maximum number
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i) //If the subscript of the maximum number is not i, swap the two
		{
			mySwap(arr[max], arr[i]);
		}
	}
}
template<typename T>
void printArray(T arr[], int len) {

	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}
void test01()
{
	//Test char array
	char charArr[] = "bdcfeagh";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}

void test02()
{
	//Test int array
	int intArr[] = { 7, 5, 8, 1, 3, 9, 2, 4, 6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main() {

	test01();
	test02();
	system("pause");
	return 0;
}

Conclusion: template can improve code reuse, which needs to be mastered skillfully

2.4 difference between ordinary function and function template

Differences between ordinary functions and function templates:

  • Automatic type conversion (implicit type conversion) can occur when ordinary function calls
  • When a function template is called, if automatic type derivation is used, implicit type conversion will not occur
  • Implicit type conversion can occur if the specified type is displayed
//Ordinary function
int myAdd01(int a, int b)
{
	return a + b;
}

//Function template
template<class T>
T myAdd02(T a, T b)  
{
	return a + b;
}

//When using function templates, if automatic type derivation is used, automatic type conversion will not occur, that is, implicit type conversion
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	
	cout << myAdd01(a, c) << endl; //Correct, implicitly convert 'c' of char type to ASCII code 99 corresponding to int type 'c'

	//myAdd02(a, c); //  An error is reported. When automatic type derivation is used, implicit type conversion will not occur

	myAdd02<int>(a, c); //Correct, if you specify a type with display, implicit type conversion can occur
}

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: it is recommended to call the function template by displaying the specified type, because you can determine the general type T yourself

2.5. Calling rules for ordinary functions and function templates

The calling rules are as follows:

  1. If both function templates and ordinary functions can be implemented, ordinary functions will be called first
  2. You can force a function template to be called through an empty template parameter list
  3. Function templates can also be overloaded
  4. If the function template can produce a better match, call the function template first

Example:

//General function and function template calling rules
void myPrint(int a, int b)
{
	cout << "Ordinary functions called" << endl;
}

template<typename T>
void myPrint(T a, T b) 
{ 
	cout << "Called template" << endl;
}

template<typename T>
void myPrint(T a, T b, T c) 
{ 
	cout << "Call overloaded template" << endl; 
}

void test01()
{
	//1. If both function templates and ordinary functions can be implemented, ordinary functions will be called first
	// Note that if you tell the compiler that there are ordinary functions, but only declare that they are not implemented or are not implemented in the current file, an error will be reported and cannot be found
	int a = 10;
	int b = 20;
	myPrint(a, b); //Call normal function

	//2. You can force a function template to be called through an empty template parameter list
	myPrint<>(a, b); //Calling function template

	//3. Function templates can also be overloaded
	int c = 30;
	myPrint(a, b, c); //Call overloaded function template

	//4. If the function template can produce a better match, call the function template first
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2); //Calling function template
}

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: since the function template is provided, it is better not to provide ordinary functions, otherwise ambiguity is likely to occur

2.6 limitations of formwork

limitations:

  • The versatility of templates is not everything
    For example:
	template<class T>
	void f(T a, T b)
	{ 
    	a = b;
    }

The assignment operation provided in the above code cannot be implemented if the passed in a and b are an array

Another example:

	template<class T>
	void f(T a, T b)
	{ 
    	if(a > b) { ... }
    }

In the above code, if the data type of T passes in a user-defined data type such as Person, it will not work normally

Therefore, in order to solve this problem, C + + provides template overloading, which can provide specific templates for these specific types

Example:

#include<iostream>
using namespace std;

#include <string>

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

//Common function template
template<class T>
bool myCompare(T& a, T& b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}


//Materialize, display the materialized prototype and definition, start with template < >, and indicate the type by name
//Concretization takes precedence over regular templates
template<> bool myCompare(Person &p1, Person &p2)
{
	if ( p1.m_Name  == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;
	//The built-in data type can directly use the general function template
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b " << endl;
	}
	else
	{
		cout << "a != b " << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	//User defined data types do not call normal function templates
	//You can create a template of materialized Person data type for special processing of this type
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2 " << endl;
	}
	else
	{
		cout << "p1 != p2 " << endl;
	}
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

Summary:

  • The generalization of user-defined types can be solved by using specific templates
  • Learning templates is not to write templates, but to use the templates provided by the system in STL

3. Class template

3.1 class template syntax

Class template function:

  • Establish a general class. The member data types in the class can be represented by a virtual type without specific formulation.
    Syntax:
template<typename T> class

Explanation:

Template - declare the creation of a template

typename - the symbol behind the surface is a data type, which can be replaced by class

T-A common data type whose name can be replaced, usually in uppercase letters
Example:

#include <string>
//Class template
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 test01()
{
	// Specify NameType as string type and AgeType as int type
	Person<string, int>P1("Sun WuKong", 999);
	P1.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: the syntax of class template is similar to that of function template. Add class after declaration template, which is called class template

3.2 difference between class template and function template

There are two main differences between class templates and function templates:

  1. Class templates do not use automatic type derivation
  2. Class templates can have default parameters in the template parameter list
    Example:
#include <string>
//Class template
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. Class templates do not use automatic type derivation
void test01()
{
	// Person p("Monkey King", 1000)// Automatic type derivation is not allowed when error class templates are used
	Person <string ,int>p("Sun WuKong", 1000); //The class template must be used in a way that displays the specified type
	p.showPerson();
}

//2. Class templates can have default parameters in the template parameter list
void test02()
{
	Person <string> p("Zhu Bajie", 999); //The template parameter list in the class template can specify default parameters
	p.showPerson();
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

Summary:

  • Class templates can only be used by displaying the specified type
  • The template parameter list in the class template can have default parameters

3.3. Creation time of member function in class template

The creation timing of member functions in class templates is different from that in ordinary classes:

  • Member functions in ordinary classes can be created from the beginning
  • Member functions in class templates are created only when called
    Example:
class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};

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

template<class T>
class MyClass
{
public:
	T obj;

	//The member functions in the class template are not created at the beginning, but are generated when the template is called

	void fun1() { obj.showPerson1(); }
	void fun2() { obj.showPerson2(); }

};

void test01()
{
	MyClass<Person1> m;
	
	m.fun1();

	//m.fun2();// There will be an error in compilation, which means that the function call will create the member function
}

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: the member functions in the class template are not created at the beginning, but only when called

3.4 making function parameters for class template objects

Learning objectives:

  • The object instantiated by the class template is the way to pass parameters to the function
    There are three ways to transfer in:
  1. Specify the type passed in - displays the data type of the object directly
  2. Parameter templating - changes parameters in an object into templates for transfer
  3. Whole class templating - pass this object type templating
    Example:
#include <string>
//Class template
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. Specifies the type of incoming
void printPerson1(Person<string, int> &p) 
{
	p.showPerson();
}
void test01()
{
	Person <string, int >p("Sun WuKong", 100);
	printPerson1(p);
}

//2. Parameter Templating
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p)
{
	p.showPerson();
	cout << "T1 The type of is: " << typeid(T1).name() << endl;
	cout << "T2 The type of is: " << typeid(T2).name() << endl;
}
void test02()
{
	Person <string, int >p("Zhu Bajie", 90);
	printPerson2(p);
}

//3. Entire class Templating
template<class T>
void printPerson3(T & p)
{
	cout << "T The type of is: " << typeid(T).name() << endl;
	p.showPerson();

}
void test03()
{
	Person <string, int >p("Tang Monk", 30);
	printPerson3(p);
}

int main() {

	test01();
	test02();
	test03();

	system("pause");

	return 0;
}

Summary:

  • Objects created through class templates can pass parameters to functions in three ways
  • The first one is widely used: specify the type of incoming

3.5 class template and inheritance

Example:

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

//class Son:public Base / / error. In c + + compilation, you need to allocate memory for subclasses. You must know the type of T in the parent class before you can inherit downward
class Son :public Base<int> //You must specify a type
{
};
void test01()
{
	Son c;
}

//The class template inherits the class template. You can use T2 to specify the T type in the parent class
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
	Son2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};

void test02()
{
	Son2<int, char> child1;
}


int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

Summary: if the parent class is a class template, the child class needs to specify the data type of T in the parent class

3.6 implementation outside class of class template member function

Learning objectives: be able to master the implementation of member functions outside the class in the class template
Example:

#include <string>

//Implementation outside class of member function in class template
template<class T1, class T2>
class Person {
public:
	//Member function class declaration
	Person(T1 name, T2 age);
	void showPerson();

public:
	T1 m_Name;
	T2 m_Age;
};

//Constructor out of class implementation
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//Out of class implementation of member functions
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "full name: " << this->m_Name << " Age:" << this->m_Age << endl;
}

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

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: when the member function in the class template is implemented outside the class, the template parameter list needs to be added

3.7 preparation of class template by document

Learning objectives:

  • Master the problems and solutions arising from the preparation of class template member function sub files

Question:

  • The creation time of the member function in the class template is in the calling stage, which leads to the link failure when writing the sub file

solve:

  • Solution 1: include directly cpp source file
  • Solution 2: write the declaration and implementation to the same file and change the suffix to hpp, hpp is the name of the contract, not mandatory

Example:

person. Code in 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;
};

//Constructor out of class implementation
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//Out of class implementation of member functions
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "full name: " << this->m_Name << " Age:" << this->m_Age << endl;
}

Class templates are written in files Code in cpp

#include<iostream>
using namespace std;

//#include "person.h"
#include "person.cpp" / / solution 1: include the cpp source file

//Solution 2: write the declaration and implementation together, and change the file suffix to hpp
#include "person.hpp"
void test01()
{
	Person<string, int> p("Tom", 10);
	p.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

Summary: the second solution is to write the class template member functions together and change the suffix to hpp

3.8 class templates and friends

Learning objectives:

  • Master the in class and out of class implementation of class template and friend function

Global function class implementation - just declare friends directly in the class

Out of class implementation of global functions - you need to let the compiler know the existence of global functions in advance

Example:

#include <string>

//2. The global function is implemented outside the friend class - first make the function template declaration. The function template definition is made below, and the friend is made
template<class T1, class T2> class Person;

//If a function template is declared, you can write the implementation to the back. Otherwise, you need to write the implementation to the front of the class for the compiler to see in advance
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p); 

template<class T1, class T2>
void printPerson2(Person<T1, T2> & p)
{
	cout << "Out of class implementation ---- full name: " << p.m_Name << " Age:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
	//1. Global functions are implemented within friend classes
	friend void printPerson(Person<T1, T2> & p)
	{
		cout << "full name: " << p.m_Name << " Age:" << p.m_Age << endl;
	}


	//The global function is implemented outside the friend class
	friend void printPerson2<>(Person<T1, T2> & p);

public:

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


private:
	T1 m_Name;
	T2 m_Age;

};

//1. Global functions are implemented within classes
void test01()
{
	Person <string, int >p("Tom", 20);
	printPerson(p);
}


//2. Global functions are implemented outside the class
void test02()
{
	Person <string, int >p("Jerry", 30);
	printPerson2(p);
}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

Summary: it is recommended to implement the global function in class, which is simple to use, and can be directly recognized by the compiler

3.9 cases of class template

Case description: implement a general array class. The requirements are as follows:

  • Data of built-in data type and user-defined data type can be stored
  • Store the data in the array to the heap
  • The capacity of the array that can be passed in the constructor
  • Provide the corresponding copy constructor and operator = to prevent shallow copy problems
  • Tail interpolation and tail deletion methods are provided to add and delete data in the array
  • The elements in the array can be accessed by subscript
  • You can get the number of current elements in the array and the capacity of the array

Example:

myArray. Code in HPP

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

template<class T>
class MyArray
{
public:
    
	//Constructor
	MyArray(int capacity)
	{
		this->m_Capacity = capacity;
		this->m_Size = 0;
		pAddress = new T[this->m_Capacity];
	}

	//copy construction 
	MyArray(const MyArray & arr)
	{
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[this->m_Capacity];
		for (int i = 0; i < this->m_Size; i++)
		{
			//If T is an object and contains a pointer, the = operator must be overloaded, because the equal sign is not constructed but assigned,
			// Ordinary types can be directly = but pointer types need deep copy
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	//Overload = operator prevents shallow copy problems
	MyArray& operator=(const MyArray& myarray) {

		if (this->pAddress != NULL) {
			delete[] this->pAddress;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		this->m_Capacity = myarray.m_Capacity;
		this->m_Size = myarray.m_Size;
		this->pAddress = new T[this->m_Capacity];
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = myarray[i];
		}
		return *this;
	}

	//Overloaded [] operator arr[0]
	T& operator [](int index)
	{
		return this->pAddress[index]; //Do not consider crossing the boundary, and the user will handle it by himself
	}

	//Tail interpolation
	void Push_back(const T & val)
	{
		if (this->m_Capacity == this->m_Size)
		{
			return;
		}
		this->pAddress[this->m_Size] = val;
		this->m_Size++;
	}

	//Tail deletion
	void Pop_back()
	{
		if (this->m_Size == 0)
		{
			return;
		}
		this->m_Size--;
	}

	//Get array capacity
	int getCapacity()
	{
		return this->m_Capacity;
	}

	//Get array size
	int	getSize()
	{
		return this->m_Size;
	}


	//Deconstruction
	~MyArray()
	{
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}
	}

private:
	T * pAddress;  //Points to a heap space that stores real data
	int m_Capacity; //capacity
	int m_Size;   // size
};

Class template case - array class encapsulation In cpp

#include "myArray.hpp"
#include <string>

void printIntArray(MyArray<int>& arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << " ";
	}
	cout << endl;
}

//Test built-in data types
void test01()
{
	MyArray<int> array1(10);
	for (int i = 0; i < 10; i++)
	{
		array1.Push_back(i);
	}
	cout << "array1 Printout:" << endl;
	printIntArray(array1);
	cout << "array1 Size of:" << array1.getSize() << endl;
	cout << "array1 Capacity:" << array1.getCapacity() << endl;

	cout << "--------------------------" << endl;

	MyArray<int> array2(array1);
	array2.Pop_back();
	cout << "array2 Printout:" << endl;
	printIntArray(array2);
	cout << "array2 Size of:" << array2.getSize() << endl;
	cout << "array2 Capacity:" << array2.getCapacity() << endl;
}

//Test custom data types
class Person {
public:
	Person() {} 
		Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
public:
	string m_Name;
	int m_Age;
};

void printPersonArray(MyArray<Person>& personArr)
{
	for (int i = 0; i < personArr.getSize(); i++) {
		cout << "full name:" << personArr[i].m_Name << " Age: " << personArr[i].m_Age << endl;
	}

}

void test02()
{
	//Create array
	MyArray<Person> pArray(10);
	Person p1("Sun WuKong", 30);
	Person p2("Han Xin", 20);
	Person p3("Daji", 18);
	Person p4("Wang Zhaojun", 15);
	Person p5("Zhao Yun", 24);

	//insert data
	pArray.Push_back(p1);
	pArray.Push_back(p2);
	pArray.Push_back(p3);
	pArray.Push_back(p4);
	pArray.Push_back(p5);

	printPersonArray(pArray);

	cout << "pArray Size of:" << pArray.getSize() << endl;
	cout << "pArray Capacity:" << pArray.getCapacity() << endl;

}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

Summary:

Be able to use the learned knowledge points to realize a general array

Topics: C++ Back-end