**** ฅ՞•ﻌ•՞ฅ̊****
[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; }