catalogue
1.1 general template functions
1.2 specialized template function
3.5 overloading of operators such as equal sign, - >, * etc
1, Experimental content
Template function (compare)
1. General template function
2. Specialized template function
2, Class template Queue
1. Class template (Queue)
2. Member template function
3. Template specialization: template function specialization, template member function specialization, and template class specialization
Three, template class AutoPtr
1. Constructor
2. Destructor
3. Copy constructor
4. Overloading of operators such as equal sign, - >, * etc
5.6 main function call AutoPtr
2, Experimental process
1. Template function
Function templates are general function descriptions. They use generics to define functions, in which generics can be replaced by specific types. By passing a type as an argument to a template, you enable the compiler to generate a function of that type. Because templates allow programs to be written in a generic (rather than a specific type), they are sometimes called general-purpose programming.
1.1 general template functions
A simple number comparison code:
template <class Type> //Using template to define the Type variable means that the Type can be any data Type int compare(const Type& v1, const Type& v2){ if(v1<v2) return -1; if(v1>v2) return 1; return 0; }
This code allows the parameters passed in to be of any data type. It does not need to consider whether the parameters passed in are integer or character type, and implement a function alone.
Test code:
int main() { cout<<compare(10,20)<<endl; cout<<compare(3.14,2.56)<<endl; }
Operation results:
1.2 specialized template function
Template specialization: when instantiating a template, special processing is performed on specific types of arguments, that is, a special instance version is instantiated. When the template is used with the formal parameters defined by specialization, the specialized version will be called. When we want to compare strings, we need to specialize the template function.
template <>//The declaration is a template specialization function int compare<const char *>(const char* const& v1, const char * const& v2) //Specialization { return strcmp(v1,v2); //Call string comparison function }//Template functions can only be written in header files, while the implementation of specialized functions can only be written in CPP
Test code:
int main() { cout<<compare("azurlane","arknight")<<endl; }
Operation results:
2. Class template Queue
Type 2.1 formwork
We need to write multiple functions with similar forms and functions, so we have function templates to reduce repetitive work; We also need to write multiple classes with similar forms and functions, so c + + introduces the concept of class template. The compiler can automatically generate multiple classes from class template, avoiding the repeated work of programmers.
Using template class allows users to define a pattern for the class, so that some data members, parameters, return values or local variables of some member functions in the class can take different types (including system predefined and user-defined).
The class template in C + + is written as follows:
template <Type parameter table> class Class template name{ Member functions and member variables };
Define a class template Queue and a management class QueueItem
template<class Type> //Type represents the general data type, that is, any return value can be taken class Queue; template<class Type> //The definition and use of class template is actually to instantiate the class template into a specific class class QueueItem { Type item; QueueItem * next; QueueItem(const Type& val):item(val),next(NULL){} //Parameter list constructor method friend Queue<Type>; //Define the queue as a friend class to facilitate access to private variables friend ostream& operator<<(ostream &os,const Queue<Type> &que); public: QueueItem<Type>* operator++() { return next; //Returns a pointer to the next element in the queue } Type & operator*() //Fetch stored elements { return item; } }; template<class Type> class Queue { public: Queue(); Queue(const Queue &q); ~Queue(); bool isEmpty(); //Determine whether the queue is empty void push(const Type& val); //Add elements to the queue void pop(); //Delete element in queue void destroy(); //Empty queue Type& front(); //The first element in the output queue void copy_item(const Queue &orig); //Copy all queues template<class It> //Member template function Queue(It beg,It end); //New parameterized constructor template<class It> void assign(It beg,It end); //Adds the specified data to the queue template<class It> void copy_items(It beg,It end); //Copy partial queue //Functions that access headers and tails const QueueItem<Type>* Head() const{return head;} const QueueItem<Type>* End() const{return(tail==NULL)? NULL:tail;} private: QueueItem<Type> * head; //Queue header pointer QueueItem<Type> * tail; //The end of queue pointer. Using the QueueItem class composed of template classes requires template declaration friend ostream& operator<<(ostream &os,const Queue<Type> &que) { os<<"< "; for(QueueItem<Type> * p = que.head;p!=NULL;p=p->next) { os<<p->item<<" "; } os<<">"; return os; } };
2.2 member template function
//Remove data from queue header template <class Type> void Queue<Type>::pop(){ QueueItem<Type> * p =head; head = head->next; delete p; //Free up space } template <class Type> void Queue<Type>::destroy() { //Delete entire queue data while(!empty()){ pop(); } } //Data is inserted at the end of the queue 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 data } else{ tail->next = pt; tail=pt; //The tail pointer needs to always point to the last element } } template<class Type> void Queue<Type>::copy_items(const Queue &orig){ for(QueueItem<Type> * pt=orig.head;pt;pt=pt->next){ push(pt->item); } } //Insert all elements of queue orig into other queues, and the original queue elements remain template <class Type> Queue<Type>& Queue<Type>::operator=(const Queue& q){ destroy(); copy_items(q); } template <class Type> template<class It> void Queue<Type>::assign(It beg, It end) { destroy(); copy_items(beg, end);//Copies the queue area of the specified range } template <class Type> template<class It> void Queue<Type>::copy_items(It beg, It end) { while(beg!=end){ push(beg); ++beg; } //Copy the queue elements of the specified range and insert them into the original queue }
The member template function should be written in the. h file.
Test code:
//Class template definition object: class template name < real data type > object name; Queue<int> xwl; int c=12; xwl.push(5);//Add 5 to the list xwl.push(c);//Add c to the list cout<<xwl<<endl; xwl.pop(); //Delete first number xwl.push(4);//Add 4 int dy=xwl.front();//Output first number cout<<dy<<endl; cout<<xwl<<endl; short a[5] = {1,4,5,7,9}; Queue<int> xwl1(a,a+5); cout<<xwl1<<endl; while(!xwl1.isEmpty()) { cout<<xwl1.front(); xwl1.pop(); } vector<int> vi(a,a+5); xwl1.assign(vi.begin()+1,vi.end()-1); //Add 4,5,7 to the list cout<<endl<<xwl1<<endl;
Experimental results:
2.3 formwork specialization
2.3.1 template function specialization
When the template we write cannot adapt to all data types, we need to specialize some functions. In order to output the desired string results, we need to specialize the string data
//Specialized push function for char * data template <> void Queue<const char *>::push(const char * const & val){ 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; } } template <> void Queue<const char*>::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 }
2.3.2 template specialization
Specialize the class template Queue for const char * data types
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;} };
Test code:
Queue<const char*> xwl; xwl.push("hi"); xwl.push("I'm"); xwl.push("wenlong xie"); cout<<endl<<xwl<<endl; xwl.pop(); string str =xwl.front(); cout<<endl<<str<<endl; cout<<endl<<xwl<<endl;
Operation results:
3. Template smart pointer
3.1 smart pointer
Smart pointers are class objects that behave like pointers, and all smart pointers will be overloaded -> and * Operator. Smart pointer is a class that stores pointers to dynamically allocated (heap) objects. It is used for lifetime control, which can ensure the automatic and correct destruction of dynamically allocated objects and prevent memory leakage. When initializing an object, an ordinary pointer cannot be directly assigned to a smart pointer, because one is a pointer and the other is a class. You can pass in a normal pointer through the constructor.
3.2 constructor
Constructors use templates like normal member functions
//Constructor to build an instance that uses a smart pointer template<class T> AutoPtr<T>::AutoPtr(T* pData) { ptr = pData; user = new int(1); //New outputs an int object and initializes it to 1; user = new int[1];new creates an array }
3.3 destructor
Generally speaking, we do not write a pile of code in the destructor, but call other functions through the destructor. Here, we release the data in the smart pointer at the same time
template<class T> AutoPtr<T>::~AutoPtr() { decrUser(); } //When the number of users decreases, you should judge whether to release variable memory (= 0) template<class T> void AutoPtr<T>::decrUser() { (*user)--; if((*user)==0) { delete ptr; ptr = 0; delete user; user = 0; } }
3.4 copy constructor
//The smart pointer assigns the initial value to the variable pointed to by another smart pointer template<class T> AutoPtr<T>::AutoPtr(const AutoPtr<T>& handle) { ptr = handle.ptr; user = handle.user; (*user)++; //Number of users of variable + 1 }
3.5 overloading of operators such as equal sign, - >, * etc
//In class T* operator->() //Overload assignment operator "- >", return ptr pointer { return ptr; } const T* operator->() const { return ptr; } T operator*() const //Overload the assignment operator "*", and take out the contents of the address pointed to by the pointer { return *ptr; } //Class external template<class T> AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& handle) //Overload assignment operator { //If you reference yourself, the number of users remains the same if(this==&handle) return *this; //Before pointing to others, the number of variable users pointed to by itself is reduced by 1 decrUser(); ptr = handle.ptr; user = handle.user; (*user)++; return *this; }
3.6 complete code
template<class T> class AutoPtr { public: AutoPtr():ptr(0),user(0){}; AutoPtr(T* pData); //Constructor AutoPtr(const AutoPtr<T>& handle); ~AutoPtr(); //Destructor AutoPtr<T>& operator=(const AutoPtr<T>& handle); //Overload assignment operator '=' void decrUser(); //Reduce the number of users T* operator->() //Overload assignment operator "- >", return ptr pointer { return ptr; } const T* operator->() const { return ptr; } T operator*() const //Overload the assignment operator "*", and take out the contents of the address pointed to by the pointer { return *ptr; } T* get() const {return ptr;} //Return saved pointer T getUser() const {return *user;}; //Returns the number of saved users private: T* ptr = 0; //Pointer to data int* user=0; //Pointer to the number of users. Use * because it needs to be modified uniformly }; //Constructor to build an instance that uses a smart pointer template<class T> AutoPtr<T>::AutoPtr(T* pData) { ptr = pData; user = new int(1); //New outputs an int object and initializes it to 1; user = new int[1];new creates an array } //The smart pointer assigns the initial value to the variable pointed to by another smart pointer template<class T> AutoPtr<T>::AutoPtr(const AutoPtr<T>& handle) { ptr = handle.ptr; user = handle.user; (*user)++; //Number of users of variable + 1 } template<class T> AutoPtr<T>::~AutoPtr() { decrUser(); } template<class T> AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& handle) //Overload assignment operator { //If you reference yourself, the number of users remains the same if(this==&handle) return *this; //Before pointing to others, the number of variable users pointed to by itself is reduced by 1 decrUser(); ptr = handle.ptr; user = handle.user; (*user)++; return *this; } //When the number of users decreases, you should judge whether to release variable memory (= 0) template<class T> void AutoPtr<T>::decrUser() { (*user)--; if((*user)==0) { delete ptr; ptr = 0; delete user; user = 0; } }
Test code:
int s1 = 15; int s2 = s1; AutoPtr<int> x1(new int(5)); AutoPtr<int> x2(new int(s1)); AutoPtr<int> x3(new int(s2)); if(x1.get()==0) { cout<<"x1 is NULL"<<endl; } cout<<"before:x1 value is: "<<x1.get()<<endl; if(x2.get()==0) { cout<<"x2 is NULL"<<endl; } cout<<"before:x2 value is: "<<x2.get()<<endl; if(x3.get()==0) { cout<<"x3 is NULL"<<endl; } cout<<"before:x3 value is: "<<x3.get()<<endl; x1=x2; cout<<"after:x1 value is: "<<x1.get()<<endl; cout<<"after:x2 value is: "<<x2.get()<<endl; x3=x1; cout<<"after:x1 value is: "<<x1.get()<<endl; cout<<"after:x3 value is: "<<x3.get()<<endl; cout<<"x3->data:"<<*x3<<endl; cout<<"x3-user:"<<x3.getUser()<<endl;
Operation results:
X1 starts to point to 5, the address is 0x711770,x2 points to 15, the address is 0x711ae0, x3 points to s2, make s2 = s1=15, the address is 0x711b20, then execute x1=x2, let X1 point to x2, the address becomes 0x711ae0, then execute x3=x1, x3 address becomes 0x711ae0, and X1 points to the same data s1=15. At this time, x1,x2,x3 all point to s1, the number of users is 3, and the life cycle ends, Execute the destructor to release three pointers.
Summary:
1. The implementation of class templates and their member functions is written in the. h file, and the specialized templates are written in the. cpp file.
2. The smart pointer automatically releases the memory space at the end of the function without manually releasing the memory space, which plays a good role in memory leakage.