Template 1
1. Why use templates?
One of the most important features of C + + is code reuse. In order to realize code reuse, the code must be universal. Common code needs to be independent of data types and can automatically adapt to changes in data types. This type of programming is called parametric programming.
therefore, C + + has the term "template". Template is a tool for C + + to support parametric programming, through which parametric polymorphism can be realized. The so-called parametric polymorphism is to parameterize the type of objects processed by the program, so that a program can be used to process many different types of objects.
2. Template definition:
template is a tool to realize the code reuse mechanism. It can realize type parameterization, that is, define types as parameters, so as to realize the real code reusability.
3. Template classification:
templates are roughly divided into two types: function templates and class templates.
the function template is for functions with different parameter types;
class template is only applicable to classes with different types of data members and member functions.
4. Purpose of using template:
let programmers write type independent code.
note: the declaration or definition of a template can only be carried out in the global, namespace or class scope. That is, it cannot be carried out within the local scope or function. For example, a template cannot be declared or defined in the main function.
2, Function template
1. What is a function template
the function template is not a real function, and the compiler cannot generate executable code for it. After defining a function template, it is only a description of the function framework. When it is implemented, its function will be determined according to the actual parameters passed.
2. Definition form of function template:
template<class Type parameter 1, class Type parameter 2, ...> Return value type name function name (parameter table) { //Definition of function body }
class can also be replaced by typename
template <typename Type parameter 1, typename Type parameter 2, ...>
3. General template function usage (compare)
for a simple example, in order to compare the values of two numbers a and B and obtain the maximum value, the following compare function needs to be used.
If the value to be compared is integer, then:
int compare(int& a, int& b) { if (a < b) { return b; } else if (b < a) { return a; } else return a; }
If the value to be compared is double floating point, then:
double compare(double& a, double& b) { if (a < b) { return b; } else if (b < a) { return a; } else return a; }
you can see that we have to rewrite the compare function for different types of variables, which is very troublesome.
so, what can we do to compare the values of different variable types by writing the compare function only once? At this time, we need to use our template function.
According to the definition form of the template function, we redefine the compare function:
T compare(T& a, T& b) { if (a < b) { return b; } else if (b < a) { return a; } else return a; }
Next, run different variable parameters through the main function:
int main() { int a = 1;//int shaping int b = 2; int max_int = compare(a, b); cout <<"int The maximum number of integers is:"<<max_int<< endl; double c = 1.11;//double floating point double d = 1.12; double max_double = compare(c, d); cout << "double The maximum number of floating point types is:" << max_double << endl; }
Operation screenshot:
You can see that through the template function, we can directly call the compare function by defining the type in the main function.
4. Usage of specialized template function
when using template functions, not all variable types are applicable. Sometimes, some special types need special treatment and cannot directly use the current template function. Therefore, we need to specialize a template function for this type (that is, write a template function for this type)
templates are also divided into full specialization and partial specialization, but function templates only have full specialization, because partial specialization can be completed through function overloading.
Full specialization of template functions:
template<>//Full specialization, empty here char compare<char>(char& a1, char& b1) { if (a1 < b1) { return b1; } else if (b1 < a1) { return a1; } else return a1; }
3, Class template (Queue)
1. What is a class template
using the template keyword, you can define not only function templates, but also class templates. Class templates represent a family of classes. They are a mechanism used to describe general data or processing methods. They enable some data members in a class and the parameters or return values of member functions to take any data type. Class templates can be used to generate classes from classes, reducing the number of class definitions.
2. Definition form of class template:
template <Type, form and parameters> class Class template name{ //Class member declaration }
To define its member function outside the class template, take the following form:
template <Type, form and parameters> Type name class name<Template parameter identifier list>::Function name(Parameter table)
3. Usage of template class
(1) , Queue, QueueItem class
as shown in the following example, the template class Queue and the template class QueueItem are defined
template<class Type> class QueueItem{ //Define partial specialization template class queueitem QueueItem(const Type &t):item(t), next(0){}//Constructor, initializing item and next, &t to make the incoming variable complete Type item; QueueItem * next; friend class Queue<Type>;//Define the friend class queue, which can access the members of the queueitem friend ostream& operator<<(ostream& os, const Queue<Type> &q); public: QueueItem<Type>* operator++(){ return next; } Type & operator*(){ return item; } }; template<class Type> class Queue { //Define partial specialization template class queue public: Queue() :head(0), tail(0) {} Queue(const Queue& q) :head(0), tail(0) { copy_items(q); } template<class It> //Define the template function and change the variable type to It Queue(It beg, It end) : head(0), tail(0) { copy_items(beg, end); } template<class It> void assign(It beg, It end); Queue& operator=(const Queue&); ~Queue() { destroy(); } Type& front() { return head->item; } const Type& front() const { return head->item; } void push(const Type&); void pop(); bool empty() const { return head == 0; } friend ostream& operator<<(ostream& os, const Queue<Type>& q) { os << "< "; QueueItem<Type>* p; for (p = q.head; p; p = p->next) { os << p->item << " "; } os << ">"; return os; } const QueueItem<Type>* Head() const { return head; } //Return to head const QueueItem<Type>* End() const { return (tail == NULL) ? NULL : tail->next; } //Recursive return tail private: QueueItem<Type>* head; QueueItem<Type>* tail; void destroy(); void copy_items(const Queue&); template<class It> void copy_items(It beg, It end); };
(2) , member template function
the code is as follows. Three member template functions are created, destroy(),pop(), and push(). The corresponding functions are to empty all data, delete the last data in the queue, and add data to the queue
template<class Type>void Queue<Type>::destroy()//Clear the data of the object { while (!empty()) { pop(); } } template<class Type> void Queue<Type>::pop() {//Delete the last of the queue QueueItem<Type>* p = head; head = head->next; delete p; } template<class Type> void Queue<Type>::push(const Type& val) { QueueItem<Type>* pt = new QueueItem<Type>(val); if (empty()) { head = tail = pt;//If the object queueitem is empty, a queue is generated for it, and the header is the current queue object } else { //If the object is not empty, this queue object is added after its queue tail->next = pt; tail = pt; } }
(3) . template specialization
we have explained what template specialization is. The specialization of template functions is different from that of template classes. Template functions can only be fully specialized, while template classes can be both fully specialized and partial specialized.
full specialization is to specialize all template types, for example, replace the Type of the above program with int;
partial specialization refers to some restrictions on template types. Partial specialization can be divided into two types: partial specialization and type range limitation.
Full specialization of template member functions:
template<> //Full specialization function, which defines the function template when it is a character type, and the header file declaration inline void Queue<const char*>::push(const char* const& val); template<> inline void Queue<const char*>::pop(); template<> void Queue<const char*>::push(const char* const& val) { char* new_item = new char[strlen(val) + 1]; strncpy(new_item, val, strlen(val) + 1); QueueItem<const char*>* pt = new QueueItem<const char*>(new_item); if (empty()) { head = tail = pt; } else { tail->next = pt; tail = pt; } } template<> void Queue<const char*>::pop() { QueueItem<const char*>* p = head; delete head->item; head = head->next; delete p; }
Template class specialization:
//Template class specialization template<> //< > is empty class Class name<Specialization type> //Specify the specific type in < > { Class member declaration };
4. Run the test Queue class
void TestQueue() { Queue<int> qt; double d = 3.3; qt.push(1); qt.push(d); //Problem: the template function does not generate a push of type double qt.push(10); cout << endl; cout << qt; short data[5] = { 0,3,6,9 }; Queue<int> qi(data, data + 5); cout << endl; cout << qi; vector<int> vi(data, data + 5); qi.assign(vi.begin(), vi.end()); cout << endl; cout << qi; Queue<const char*> q1; q1.push("I'm"); q1.push("come"); q1.push("from"); q1.push("JMU"); cout << endl; cout << q1; Queue<const char*> q2(q1); cout << q2; }
Operation screenshot:
4, Class template implementation (AutoPtr class)
1. Constructor
template<class T> AutoPtr<T>::AutoPtr(T* pData) { m_pData = pData; m_nUser = new int(1); }
2. Destructor
~AutoPtr() { decrUser(); } void decrUser(); template<class T> void AutoPtr<T>::decrUser() { --(*m_nUser); if ((*m_nUser) == 0) { delete m_pData; m_pData = 0; delete m_nUser; m_nUser = 0; } }
3. Copy constructor
template<class T> AutoPtr<T>::AutoPtr(const AutoPtr<T>& h) { m_pData = h.m_pData; m_nUser = h.m_nUser; (*m_nUser)++; }
4. Overloading of operators such as equal sign, - >, * etc
Smart pointers can be operated with "*" and "- >" like ordinary pointers
AutoPtr<T>& operator=(const AutoPtr<T>& h); T* operator->() { return m_pData; } T& operator*() { return *m_pData; } const T& operator *()const { return *m_pData; } const T* operator ->()const { return m_pData; } template<class T> AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h) { decrUser(); m_pData = h.m_pData; m_nUser = h.m_nUser; (*m_nUser)++; }
5. Main function call AutoPtr
#include<iostream> #include <vector> #include "autoptr.h" #include "CMatrix.h" using namespace std; int main() { AutoPtr<CMatrix> h1; double data[6] = {1,2,3,4,5,6}; h1->Create(2,3,data); cout << *h1 << endl; AutoPtr<CMatrix> h2(h1); (*h2).Set(0,1,10); cout << *h1 << endl << *h2; }
6. Screenshot of operation implementation
It can be seen that h2 is created through the copy constructor (copy h1), so what h2 changes after calling the Set method is the value of the same address, so h1 also changes, resulting in the same output results of h1 and h2.