C++模板:类模板和类模板的友元【C++模板】(57)
- 類模板
- Stack 類
- 類模板
- 格式
- 類模板的應(yīng)用
- 類模板的多文件實現(xiàn)
- 類模板的友元
- 類模板中的友元在.h
- 類模板中的友元在.cpp
- hpp
- STL入門
類模板
Stack 類
我們先給出我們之前實現(xiàn)的棧結(jié)構(gòu):
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h> using namespace std; class Stack { public:Stack(int size = 1024){space = new int[size];top = 0;}~Stack() {delete[]space;}bool isEmpty() {return top == 0;}bool isFull() {return top == 1024;}void push(int data) {space[top++] = data;}int pop() {return space[--top];} private:int* space;int top; }; int main() {Stack s(100);for (int i = 0; i < 10; ++i) {if (!s.isFull())s.push(i);}while (!s.isEmpty())cout << s.pop() << endl;return 0; }運行結(jié)果為:
上面代碼中如果我們要對于棧結(jié)構(gòu)進行泛化,最主要的就是對于int * space 存儲空間的泛化。
Stack 類模板化,可以 push 和 pop 不同的數(shù)據(jù)類型。主要由幾個因素需要把控。
只需要保持棧中的空間元素類型,壓入元素類型,彈出元素類型,三者保持一致就能夠?qū)崿F(xiàn)泛化。
類模板
格式
template<typename T> class ClassName {void func(T ); };template<typename T> void ClassName<T>::func(T) {}類模板的應(yīng)用
我們在函數(shù)模板中使用的時候先寫函數(shù)模板,然后進行函數(shù)模板的實例化形成模板函數(shù),然后對于實例化之后的模板函數(shù)進行調(diào)用。
myswap() --> myswap() --> myswap()(a,b)
函數(shù)模板 --> 模板函數(shù) --> 函數(shù)調(diào)用
對比到類模板就是:
類模板 --> 模板類 --> 類對象的創(chuàng)建
stack --> stack --> stack s(100)
我們現(xiàn)在對于上面的棧類進行泛化:
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h>using namespace std;template <typename T>class Stack { public:Stack(int size = 1024){space = new T[size];top = 0;}~Stack() {delete[]space;}bool isEmpty() {return top == 0;}bool isFull() {return top == 1024;}void push(T data) {space[top++] = data;}T pop() {return space[--top];} private:T* space;int top; }; int main() {Stack<int> s;//類模板的實例化 生成模板類 并且創(chuàng)建類對象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 11);}while(!s.isEmpty()){cout << s.pop() << endl;}return 0; }運行結(jié)果為:
類內(nèi)函數(shù)的聲明和定義分開的實現(xiàn):
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h>using namespace std;template <typename T> //類模板 class Stack { public:Stack(int size = 1024);~Stack();bool isEmpty();bool isFull();void push(T data);T pop();private:T* space;int top; };template <typename T> Stack<T>::Stack(int size) {space = new T[size];top = 0; }template <typename T> Stack<T>::~Stack() {delete[]space; }template <typename T> bool Stack<T>::isEmpty() {return top == 0; }template <typename T> bool Stack<T>::isFull() {return top == 1024; }template <typename T> void Stack<T>::push(T data) {space[top++] = data; }template <typename T> T Stack<T>::pop() {return space[--top]; }int main() {Stack<int> s;//類模板的實例化 生成模板類 并且創(chuàng)建類對象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 10);}while (!s.isEmpty()){cout << s.pop() << endl;}return 0; }運行結(jié)果為:
類模板的多文件實現(xiàn)
main.cpp
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h> #include "mystack.h" #include "mystack.cpp" using namespace std;int main() {Stack<int> s;//類模板的實例化 生成模板類 并且創(chuàng)建類對象for (int i = 0; i < 10; i++){if (!s.isFull())s.push(i * 10);}while (!s.isEmpty()){cout << s.pop() << endl;}return 0; }mystack.cpp
#include "mystack.h"template <typename T> Stack<T>::Stack(int size) {space = new T[size];top = 0; }template <typename T> Stack<T>::~Stack() {delete[]space; }template <typename T> bool Stack<T>::isEmpty() {return top == 0; }template <typename T> bool Stack<T>::isFull() {return top == 1024; }template <typename T> void Stack<T>::push(T data) {space[top++] = data; }template <typename T> T Stack<T>::pop() {return space[--top]; }mystack.h
#pragma once template <typename T> //類模板 class Stack { public:Stack(int size = 1024);~Stack();bool isEmpty();bool isFull();void push(T data);T pop();private:T* space;int top; };運行結(jié)果為:
這里需要強調(diào)的是,我們在實現(xiàn)多文件變成的時候并不是簡單的把之前類內(nèi)函數(shù)的定義和實現(xiàn)分開,然后在多文件中編寫,而是需要注意需要在main函數(shù)中加入#include “mystack.cpp”才能夠編譯成功和運行。
那么為什么是需要加上#include “mystack.cpp”
我們在類模板的友元中進行說明。
類模板的友元
類模板中的友元在.h
友元函數(shù),實現(xiàn)在.h 文件中并不多見,但在模板中這樣使用友元,就是一種常規(guī)則用法。
main.cpp
#include <iostream> #include "mylist.h" #include "mylist.cpp"using namespace std; using namespace listspace;int main() {GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0; }mylist.cpp
#ifndef __MYLIST_ CPP__ #define __MYLIST_ CPP__ #include <iostream> #include <cstdlib> #include "mylist.h"using namespace std; namespace listspace {template<class ItemType>GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0){_item = new ItemType[max];}template<class ItemType>GenericList<ItemType>::~GenericList(){delete[] _item;}template<class ItemType>int GenericList<ItemType>::length() const{return (_curIdx);}template<class ItemType>void GenericList<ItemType>::add(ItemType new_item){if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;}}template<class ItemType>bool GenericList<ItemType>::full() const{return (_curIdx == _maxLength);}template<class ItemType>void GenericList<ItemType>::erase(){_curIdx = 0;} } #endifmylist.h
#ifndef __MYLIST_H H__ #define __MYLIST_H H__ #include <iostream> #include <ostream> using namespace std;namespace listspace { template<class ItemType>class GenericList { public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();friend ostream & operator<<(ostream & out,GenericList<ItemType>& list){for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out;}private:ItemType* _item;int _maxLength;int _curIdx;}; }//listspace #endif //__MYLIST_H__運行結(jié)果為:
使用類模板的友元時,在模板的.h文件類的聲明處定義和實現(xiàn)友元函數(shù)。
類模板中的友元在.cpp
mylist.cpp
#ifndef __MYLIST_ CPP__ #define __MYLIST_ CPP__ #include <iostream> #include <cstdlib> #include "mylist.h"using namespace std; namespace listspace {template<class ItemType>GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0){_item = new ItemType[max];}template<class ItemType>GenericList<ItemType>::~GenericList(){delete[] _item;}template<class ItemType>int GenericList<ItemType>::length() const{return (_curIdx);}template<class ItemType>void GenericList<ItemType>::add(ItemType new_item){if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;}}template<class ItemType>bool GenericList<ItemType>::full() const{return (_curIdx == _maxLength);}template<class ItemType>void GenericList<ItemType>::erase(){_curIdx = 0;}template<class ItemType>ostream & operator<< (ostream& out, GenericList<ItemType> & list){for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out;} } #endifmylist.h
#ifndef __MYLIST_H H__ #define __MYLIST_H H__ #include <iostream> #include <ostream> using namespace std; namespace listspace {template<class ItemType>class GenericList;template<class ItemType>ostream & operator<<(ostream& out, GenericList<ItemType>& list);template<class ItemType> class GenericList { public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();//友元函數(shù)聲明friend ostream& operator<< <>(ostream& out, GenericList<ItemType>& list);private:ItemType* _item;int _maxLength;int _curIdx;}; }//listspace #endif //__MYLIST_H__main.cpp
#include <iostream> #include "mylist.h" #include "mylist.cpp"using namespace std; using namespace listspace; int main() {GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0; }運行結(jié)果為:
在類模板的 .cpp 文件中實現(xiàn)友元的時候需要進行以下操作:
① 在類中聲明<> 表明是一個空體聲明。
② 在類外實現(xiàn),和其他函數(shù)實現(xiàn)相同,需要加上類型模板。
③ 在類的聲明的前面,對類模板的友元函數(shù)作前向聲明。并且在其前面作類的前向聲明。
所以一般建議讀者在.h文件中實現(xiàn)類模板的友元。
但是呢,我們平時在使用到時候很少會有#include “mylist.cpp”這樣的操作,所以我們引入hpp
hpp
由于編譯器需要通過這些"模板"為實例化類型生成實際的方法代碼,因此任何使用了模板的源代碼文件中,編譯器都應(yīng)該能同時訪問類模板定義和方法定義。
C++中的編譯是以文件為單位的,然后鏈接階段完成鏈接。如果模板的聲明與實現(xiàn)分開,這種機制顯然會導(dǎo)致看不到模板的全貌,而致編譯失敗。所以常將類模板定義和方法定義放到一起,該類模板文件的后綴常為.hpp,以示與普通文件的區(qū)別。在使用的時候,#include"xx.hpp"。
代碼演示:
mylist.hpp
#ifndef __MYLIST_H H__ #define __MYLIST_H H__ #include <iostream> #include <ostream> using namespace std;namespace listspace {template<class ItemType>class GenericList;template<class ItemType>ostream & operator<<(ostream& out, GenericList<ItemType>& list);template<class ItemType>class GenericList{public:GenericList(int max);~GenericList();int length() const;void add(ItemType new_item);bool full() const;void erase();friend ostream& operator<< <>(ostream& out, GenericList<ItemType>& list);private:ItemType* _item;int _maxLength;int _curIdx;}; }//listspace #endif //__MYLIST_H__#ifndef __MYLIST_ CPP__ #define __MYLIST_ CPP__ #include <iostream> #include <cstdlib> #include "mylist.h"using namespace std; namespace listspace { template<class ItemType> GenericList<ItemType>::GenericList(int max): _maxLength(max), _curIdx(0) {_item = new ItemType[max]; } template<class ItemType> GenericList<ItemType>::~GenericList() {delete[] _item; } template<class ItemType> int GenericList<ItemType>::length() const {return (_curIdx); } template<class ItemType> void GenericList<ItemType>::add(ItemType new_item) {if (full()){cout << "Error: adding to a full list.\n";exit(1);}else{_item[_curIdx] = new_item;_curIdx = _curIdx + 1;} } template<class ItemType> bool GenericList<ItemType>::full() const {return (_curIdx == _maxLength); } template<class ItemType> void GenericList<ItemType>::erase() {_curIdx = 0; }template<class ItemType> ostream& operator<< (ostream& out, GenericList<ItemType>& list) {for (int i = 0; i < list._curIdx; i++){out << list._item[i];}return out; } } #endifmain.cpp
#include <iostream> #include "mylist.hpp"using namespace std; using namespace listspace; int main() {GenericList<int> first_list(2);first_list.add(1);first_list.add(2);cout <<"first_list"<< first_list << endl;GenericList<char> second_list(10);second_list.add('A');second_list.add('B');second_list.add('C');cout << second_list<<second_list << endl;return 0; }運行結(jié)果為:
模板通過會將聲明和實現(xiàn)放在一個文件中,及就是.hpp中。這樣做的原因就是,C語言和C++的編譯模式是按照文件進行編譯的。
模板在進行實例化的時候,是需要看到整個模板的全部內(nèi)容,及就是模板的定義和實現(xiàn)的全部代碼。所以如果只有.h的話,實例化的時候僅僅只是看到模板的聲明,并不能看到模板的實現(xiàn),所以需要使用.hpp來實現(xiàn)。把模板的聲明和實現(xiàn)放在一起。
也就是說在任何需要實例化的地方,都需要看到模板的全部的聲明和定義。
STL入門
代碼演示:
#include <iostream> #include <vector> #include <stdlib.h> #include <time.h> #include <algorithm> using namespace std;int main() {srand(time(nullptr));vector<int> vi; //類模板的實例化,生成模板類,并且創(chuàng)建類對象 vifor (int i = 0; i < 10; i++){vi.push_back(rand() % 100);}sort(vi.begin(), vi.end(), [](int x, int y) {return x < y; });for (auto i : vi)cout << i << endl;return 0; }運行結(jié)果:
引入內(nèi)存管理機制,一開始不需要指定大小。
總結(jié)
以上是生活随笔為你收集整理的C++模板:类模板和类模板的友元【C++模板】(57)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++模板:模板简述,函数模板详细说明【
- 下一篇: C++赋值运算符重载【C++赋值运算符重