[4] Let's Review C + +!

**** ฅ՞•ﻌ•՞ฅ̊****
[4] Generic programming and STL!

Template

Function template function:
To establish a general function, the function return value type and parameter type can be represented by a virtual type.

Syntax:
template

Function declaration or definition
Explanation:

Template - declare create template
typename -- the symbol after the surface is a data type, which can be replaced by class
T - General data type, name can be replaced, usually uppercase

Summary:
Function template uses the keyword template
There are two ways to use function templates: automatic type derivation and display of specified types
The purpose of templates is to improve reusability and parameterize types

Example:

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

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

//Using template to provide general exchange function
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 with templates
	//1. Automatic type derivation
	mySwap(a, b);

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

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

}

int main() {

	test01();

	system("pause");

	return 0;
}

Function template considerations

matters needing attention:

For automatic type derivation, the consistent data type T must be derived before use
The template must determine the data type of T before it can be used

Example:

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


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

	mySwap(a, b); // Correct, consistent T can be derived
	//mySwap(a, c); / / error, inconsistent T type derivation
}


// 2. The template must determine the data type of T before it can be used
template<class T>
void func()
{
	cout << "func call" << endl;
}

void test02()
{
	//func(); / / error. Templates cannot be used independently. You must determine the type of T
	func<int>(); //The template can only be used by giving T a type by displaying the specified type
}

int main() {

	test01();
	test02();

	system("pause");

	return 0;
}

Function template case

Case description:

Using function template to encapsulate a sort function, you can sort arrays of different data types
The sorting rule is from large to small, and the sorting algorithm is selective sorting
Test with char array and int array respectively
Example:

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


template<class T> // Can also be replaced with typename
//Sorting the array from large to small by selecting sorting
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, exchange 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;
}

The difference between ordinary function and function template

Automatic type conversion (implicit type conversion) can occur when a normal function is called
When a function template is called, if automatic type derivation is used, no implicit type conversion will occur
Implicit type conversion can occur if the specified type is displayed
Example:

//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 you use automatic type inference, 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 of 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, implicit type conversion can occur if the specified type is displayed
}

int main() {

	test01();

	system("pause");

	return 0;
}

Call rules of common functions and function templates

The call rules are as follows:

If both the function template and the ordinary function can be implemented, the ordinary function will be called first
The function template can be forced to be called through the empty template parameter list
Function templates can also be overloaded
If the function template can produce a better match, call the function template first

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

//General function and function template call rules
void myPrint(int a, int b)
{
	cout << "Normal function 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 the function template and the ordinary function can be implemented, the ordinary function will be called first
	// Note that if you tell the compiler that there are ordinary functions, but only the declaration is not implemented, or is not implemented in the current file, you will not find the error
	int a = 10;
	int b = 20;
	myPrint(a, b); //Call normal function

	//2. The function template can be forced to be called through the empty template parameter list
	myPrint<>(a, b); //Call 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); //Call function template
}

int main() {

	test01();

	system("pause");

	return 0;
}

Limitations of formwork

limitations:

The universality of template is not omnipotent
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 is passed in a custom data type like Person, it will not work properly

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

Summary:
Using concrete template, we can solve the generalization of custom type
The purpose of learning template is not to write template, but to use the template provided by the system in STL
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;
	}
}


//Materialization, showing that the stereotype and definition of the materialization start with template < > and indicate the type by name
//Materialization 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);
	//Custom data type, do not call ordinary function template
	//You can create a concrete template of the Person data type, which is used 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;
}

Class template

Class template syntax
Class template function:

Create a general class. The member data types in the class can be represented by a virtual type without specific formulation.

Syntax:
template

Explanation:
Template - declare create template
typename -- the symbol after the surface is a data type, which can be replaced by class
T - General data type, name can be replaced, usually uppercase

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, AgeType as int type
	Person<string, int>P1("Sun WuKong", 999);
	P1.showPerson();
}

int main() {

	test01();

	system("pause");

	return 0;
}

Difference between class template and function template

There are two main differences between class template and function template:
Class template has no usage of automatic type derivation
Class templates can have default parameters in the template parameter list

Summary:

Class template can only be used to display the specified type method
The template parameter list in the class template can have default parameters

#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 template has no usage of automatic type derivation
void test01()
{
	// Person p("Monkey King", 1000); / / when an error class template is used, automatic type derivation is not allowed
	Person <string ,int>p("Sun WuKong", 1000); //Class template must be used to display 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;
}

Creating time of member function in class template

There is a difference between the time when a member function is created in a class template and in a normal class

Member functions in common classes can be created from the beginning
Member functions in a class template 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(); / / a compilation error will occur, indicating that the function call will create the member function
}

int main() {

	test01();

	system("pause");

	return 0;
}

Class template object as function parameter

Learning objectives:

Class template instantiated object, way to pass parameters to function
There are three ways of incoming:

Specify the type of incoming - display the data type of the object directly
Parameter templating - changes parameters in an object to a template for transmission
Templating the entire class - templating and passing this object type

Summary:
Objects created through class templates can pass parameters to functions in three ways
More widely used is the first: specify the type of incoming
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. Specify 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 types of are: " << typeid(T1).name() << endl;
	cout << "T2 The types of are: " << typeid(T2).name() << endl;
}
void test02()
{
	Person <string, int >p("Zhu Bajie", 90);
	printPerson2(p);
}

//3. Templating the entire class
template<class T>
void printPerson3(T & p)
{
	cout << "T The types of are: " << 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;
}

Class template member function out of class implementation

Example:

#include <string>

//Out of class implementation of member function in class template
template<class T1, class T2>
class Person {
public:
	//Declaration within member function class
	Person(T1 name, T2 age);
	void showPerson();

public:
	T1 m_Name;
	T2 m_Age;
};

//Implementation outside constructor class
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 function
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: template parameter list needs to be added when implementing member function out of class in class template

Class template preparation by file

The creation time of member function in class template is in the call phase, which results in the link failure when writing sub files
solve:

Solution 1: include the. cpp source file directly
Solution 2: write the declaration and implementation to the same file, and change the suffix name to. hpp. hpp is the agreed name, not mandatory
Conclusion: it is suggested that the global function be implemented in class, which is easy to use and can be directly recognized by the compiler
Class template case

Example:

person.hpp Medium code:

#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;
};

//Implementation outside constructor class
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 function
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
	cout << "full name: " << this->m_Name << " Age:" << this->m_Age << endl;
}
//Class template preparation by file.cpp Medium code

#include<iostream>
using namespace std;

//#include "person.h"
#include "person.cpp" //Solution 1, including 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 main solution is the second one. Write the class template member functions together and change the suffix name to. hpp

Class templates and friends

Learning objectives:

Master the implementation of class template and friend function in and out of class
Intra class implementation of global functions

Out of class implementation of global functions - the compiler needs to 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, and then make the function template definition and friend
template<class T1, class T2> class Person;

//If the function template is declared, you can write the implementation to the back. Otherwise, you need to write the implementation body to the front of the class so that the compiler can see it 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. Implementation of global function with friend class
	friend void printPerson(Person<T1, T2> & p)
	{
		cout << "full name: " << p.m_Name << " Age:" << p.m_Age << endl;
	}


	//The implementation of global function with 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 implemented in class
void test01()
{
	Person <string, int >p("Tom", 20);
	printPerson(p);
}


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

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

Implement a general array class

Data of built-in data type and custom data type can be stored
Store the data in the array to the heap
The capacity of arrays that can be passed in the constructor
Provide the corresponding copy constructor and operator = to prevent shallow copy problems
Provide tail insertion and tail deletion to add and delete data in the array
Elements in an 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.hpp Medium code

#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 pointers, you must overload the = operator, because this equals sign is not a construction but an assignment,
			// Normal type can be direct = but pointer type needs deep copy
			this->pAddress[i] = arr.pAddress[i];
		}
	}

	//Overload = operators prevent 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;
	}

	//Overload [] operator arr[0]
	T& operator [](int index)
	{
		return this->pAddress[index]; //Do not consider cross-border, users will handle it by themselves
	}

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

	//Censoring
	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;  //Point to a heap space that stores real data
	int m_Capacity; //capacity
	int m_Size;   // size
};

Class template case - in array class encapsulation.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 of:" << 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 of:" << 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 of:" << pArray.getCapacity() << endl;

}

int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}

Tags: P4 Programming ascii

Posted on Fri, 19 Jun 2020 00:38:19 -0400 by mattkenefick