Experimental background
Based on the C + + environment under Qt Creator, this experiment realizes the unique template function of C + +. One of the most important features of C + + is code reuse. In order to realize the universality of the code, it is necessary to make the code not affected by the data type, and can automatically adapt to the change of data type. This is parametric programming. Template is one of the tools of parametric programming, which can realize parameter polymorphism. The so-called parameter 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.
1. Template function
Template function definition form:
Template < template parameter table >
Type name function name (parameter table)
{
Definition of function body
}
The definition of all function templates starts with the keyword template. The function parameter table consists of comma separated template parameters and can include the following contents
1.class (or typename) identifier, indicating that a type parameter can be accepted. These type parameters represent types, which can be predefined types or custom types.
2. type specifier identifier, indicating that a constant of the type specified by type specifier can be accepted as the parameter.
3. Template < parameter table > class identifier, indicating that a class template name can be received as a parameter.
It looks a little confused. Let's give a specific example
1.1. General template function
If we want to compare the size of data, we usually use the compare function. If we want to compare int and char data types, we need to define functions with two input parameters.
//int type comparison int compare(const int v1,const int v2) { if(v1>v2) return 1; else if(v1<v2) return -1; else return 0; } //char type comparison int compare(const char v1,const char v2) { if(v1>v2) return 1; else if(v1<v2) return -1; else return 0; } #endif // COMPARE_H
Main function:
int main() { int a=20,b=10; char m='m',x='x'; cout<<compare(a,b)<<endl; cout<<compare(m,x)<<endl; return 0; }
Compare different data types through function overloading, but it will be troublesome if more data types need to be compared. The template function only needs one function to solve all function types.
Template function:
template <typename T> //The int before compare is because the return values are 1, - 1, 0. If you want to return a value of the same type as the input, change int to T int compare(const T v1,const T v2) { if(v1>v2) return 1; else if(v1<v2) return -1; else return 0; }
In this way, no matter what type of data it is, it can be compared with this function.
Summary:
1. The type parameter t in the function template represents an abstract type. T has no meaning but a code. When the compiler detects that the compare function is called, it will replace t in the entire template definition with the type of the first argument, reconstruct it into a complete function, and then compile the new function.
2. Function templates are closely related to overloading. The related functions generated from the function template are of the same name, and the compiler calls the corresponding functions with overloaded solutions.
1.2. Specialized template function
Although templates have so many advantages, we should clearly realize that there can be no one way to solve all problems in the world. Therefore, it is necessary to introduce specialized templates to make adjustments for certain types.
//Declaration in header file template <> int compare<char*>(char*v1,char*v2);
//cpp file implementation template<> int compare<char*>(char*v1,char*v2) { return strcmp(v1,v2); }
This enables the comparison of statements, and the specialization declaration should be placed under the template function.
Precautions for function template:
1. The function template itself will not generate any object code during compilation, and only the instances generated by the template will generate object code.
2. The function template referenced by multiple source files should be placed in the header file together with the function body, rather than just the declaration in the header file like ordinary functions.
3. The function pointer can only point to the instance of the template, not the template itself.
2. Template class Queue
The syntax form of class template declaration is:
Template < template parameter table >
class name
{
Class member declaration
}
A typical example is vector. This container is a class template that can store or read variables of any type. This is the purpose of using the class template. You can use the same method for any type of object without setting it separately.
2.1. Formwork
//Queue element template <class Type> class QueueItem { Type item;//Store data QueueItem * next;//The address of the next element in the queue QueueItem(const Type & data):item(data),next(0){};//Parameter list construction friend class Queue<Type>;//Friend authorization friend ostream& operator<<(ostream& os, const Queue<Type> & q); //Overloaded output element operator public: QueueItem<Type>* operator++()//Returns the address of the next element in the queue { return next; } Type & operator*() //Fetch stored elements { return item; } };
//queue template <class Type> class Queue { private: void copy_items(const Queue &orig); //Copy start element QueueItem<Type>* head;//Queue header pointer QueueItem<Type>* tail;//End of queue pointer void destroy(); //Free queue space template<class It> void copy_items(It beg, It end); //Copies the queue elements within the specified range public: Queue():head(0),tail(0){}; //Parameter list construction, initialization of head pointer and tail pointer Queue(const Queue& q):head(0),tail(0){ copy_items(q); //copy construction } template<class It> Queue(It beg, It end):head(0),tail(0){copy_items(beg,end);} //Specify range copy construction template<class It> void assign(It beg, It end); Queue& operator=(const Queue&); ~Queue(){destroy();} //Destructor Type& front(){return head->item;} //Return to the head of the team void push(const Type&); //Join the team void pop(); //Out of the team bool empty() const{return head==0;} //Determine whether the queue element is empty 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; } //Functions that access headers and tails const QueueItem<Type>* Head() const{return head;} const QueueItem<Type>* End() const {return(tail==NULL)?NULL:tail;} };
2.2. Member template function
Like template functions, member template functions provide methods that can be used of any type
//Out of the team template <class Type> void Queue<Type>::pop(){ QueueItem<Type> * p =head; head = head->next; delete p; //Free up space } //Delete queue template <class Type> void Queue<Type>::destroy() { while(!empty()){ pop(); } } //Join the team template <class Type> void Queue<Type>::push(const Type& val){ QueueItem<Type> * pt = new QueueItem<Type>(val); if(empty()){ head = tail = pt; //The head and tail point to the same address } else{ tail->next = pt; tail=pt; //The tail pointer needs to always point to the last element } } //Insert all elements of queue orig into other queues, and the original queue elements remain template<class Type> void Queue<Type>::copy_items(const Queue &orig){ for(QueueItem<Type> * pt=orig.head;pt;pt=pt->next){ push(pt->item); } } template <class Type> Queue<Type>& Queue<Type>::operator=(const Queue& q){ destroy(); copy_items(q); } //Copies the queue area of the specified range template <class Type> template<class It> void Queue<Type>::assign(It beg, It end) { destroy(); copy_items(beg, end); } //Copy the queue elements of the specified range and insert them into the original queue template <class Type> template<class It> void Queue<Type>::copy_items(It beg, It end) { while(beg!=end){ push(beg); ++beg; } }
2.3. Template specialization
Template member function specialization is the same as template function specialization. Header file declaration, cpp file implementation, and specialization type are declared in < >
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 Queue<const char *> { private: void copy_items(const Queue &orig); //Copy start element QueueItem<const char *>* head;//The queue needs two pointers at the beginning and end QueueItem<const char *>* tail;//Using a QueueItem class consisting of a template class requires a template declaration void destroy(); //Free queue space template<class It> void copy_items(It beg, It end); //Specifies the range of copy queue elements public: Queue():head(0),tail(0){}; //Parameter list constructor, initializing head pointer and tail pointer Queue(const Queue& q):head(0),tail(0){ copy_items(q); //copy constructor } template<class It> Queue(It beg, It end):head(0),tail(0){copy_items(beg,end);} //Specifies the scope copy constructor template<class It> void assign(It beg, It end); Queue& operator=(const Queue&); ~Queue(){destroy();} //Destructor const char *& front(){return head->item;} //Returns the top of the queue void push(const char *&val){ //Specialize const char * template function char* new_item = new char[strlen(val)+1]; //Create a character array based on the length of the string strncpy(new_item,val,strlen(val)+1); //Copy Character content QueueItem<const char* >*pt = new QueueItem<const char *>(new_item); //Declare template specialization char * class if(empty()){ head = tail = pt; } else{ tail->next = pt; tail = pt; } }; //Put elements in queue void pop(){ QueueItem<const char *> *p = head; //Specialization template class QueueItem delete head->item;//char * data needs to be managed by itself, so it can be released by itself head = head->next; delete p; //Freeing pointer space }; //Remove queue header element bool empty() const{return head==0;} //Determine whether the queue element is empty friend ostream& operator<<(ostream& os, const Queue<const char *> &q) { os<<"< "; QueueItem<const char *> * p; for(p=q.head;p;p=p->next) { os<<p->item<<" "; } os<<">"; return os; } //Functions that access headers and tails const QueueItem<const char *>* Head() const{return head;} const QueueItem<const char *>* End() const {return(tail==NULL)?NULL:tail;} };
3. Template AutoPtr
For C and C + +, the use of pointers is both a feature and a weakness. Improper use of pointers may lead to a series of problems, such as memory overflow, data leakage and so on. Therefore, how to use pointers more conveniently is the goal of AutoPtr (smart pointer).
3.1. Constructor
template<class T> AutoPtr<T>::AutoPtr(T* pData) { m_pData = pData; m_nUser = new int(1); }
3.2. Destructor
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.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)++; }
3.4. Overloading of operators such as equal sign, - >, * etc
=Heavy load
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h) { decrUser(); m_pData = h.m_pData; m_nUser = h.m_nUser; (*m_nUser)++; }
->Heavy load
T* operator->() { return m_pData; } const T* operator ->() const{ return m_pData; }
*Heavy load
T& operator*() { return *m_pData; } const T& operator *() const{ return *m_pData; }
3.5. Main function call AutoPtr
void TestAutoPtr(); int main() { TestAutoPtr(); return 0; } void TestAutoPtr() { 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; }
h2 is created by copying the constructor. After h2 calls the Set method, it changes the value of the same address, so the final output results of h1 and h2 are the same.