類模板
Stack 類
我們先給出我們之前實現的棧結構:
#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;
}
運行結果爲:
上面代碼中如果我們要對於棧結構進行泛化,最主要的就是對於int * space 存儲空間的泛化。
Stack 類模板化,可以 push 和 pop 不同的數據類型。主要由幾個因素需要把控。
那麼只需要保持棧中的空間元素類型,壓入元素類型,彈出元素類型,三者保持一致就能夠實現泛化。
我們在函數模板中使用的時候先寫函數模板,然後進行函數模板的實例化形成模板函數,然後對於實例化之後的模板函數進行調用。
myswap() --> myswap() --> myswap()(a,b)
函數模板 --> 模板函數 --> 函數調用
對比到類模板就是:
類模板 --> 模板類 --> 類對象的創建
stack --> stack --> stack s(100)
類模板格式
template<typename T> class ClassName
{
void func(T );
};
template<typename T> void ClassName<T>::func(T)
{
}
類模板的應用
我們現在對於上面的棧類進行泛化:
#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;//類模板的實例化 生成模板類 並且創建類對象
for (int i = 0; i < 10; i++)
{
if (!s.isFull())
s.push(i * 11);
}
while(!s.isEmpty())
{
cout << s.pop() << endl;
}
return 0;
}
運行結果爲:
類內函數的聲明和定義分開的實現:
#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;
void push(T data);
}
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;//類模板的實例化 生成模板類 並且創建類對象
for (int i = 0; i < 10; i++)
{
if (!s.isFull())
s.push(i * 10);
}
while (!s.isEmpty())
{
cout << s.pop() << endl;
}
return 0;
}
運行結果爲:
類模板的多文件實現
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;//類模板的實例化 生成模板類 並且創建類對象
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;
void push(T data);
}
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;
};
運行結果爲:
這裏需要強調的是,我們在實現多文件變成的時候並不是簡單的把之前類內函數的定義和實現分開,然後在多文件中編寫,而是需要注意需要在main函數中加入#include “mystack.cpp”才能夠編譯成功和運行。
那麼爲什麼是需要加上#include “mystack.cpp”
我們在類模板的友元中進行說明。
類模板的友元
類模板中的友元在.h
友元函數,實現在.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;
}
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;
}
}
#endif
mylist.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__
運行結果爲:
類模板中的友元在.cpp
友元函數,實現在.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;
}
}
#endif
mylist.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();
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;
}
運行結果爲:
在類完中的cpp實現的時候需要進行以下操作:
① 在類中聲明<> 表明是一個空體聲明
② 在類外實現 和其他函數實現相同 需要加上類型模板
③ 在類的聲明的前面,對類模板的友元函數作前向聲明。並且在其前面作類的前向聲明
所以一般建議讀者在.h文件中實現類模板的友元
但是呢,我們平時在使用到時候很少會有#include “mylist.cpp”這樣的操作,所以我們引入hpp
hpp
由於編譯器需要通過這些"模板"爲實例化類型生成實際的方法代碼,因此任何使用了模板的源代碼文件中,編譯器都應該能同時訪問類模板定義和方法定義。
C++中的編譯是以文件爲單位的,然後鏈接階段完成鏈接。如果模板的聲明與實現分開,這種機制顯然會導致看不到模板的全貌,而致編譯失敗。所以常將類模板定義和方法定義放到一起,該類模板文件的後綴常爲.hpp,以示與普通文件的區別。在使用的時候,#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;
}
}
#endif
main.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;
}
運行結果爲:
模板通過會將聲明和實現放在一個文件中,及就是.hpp中。這樣做的原因就是,C語言和C++的編譯模式是按照文件進行編譯的。模板在進行實例化的時候,是需要看到整個模板的,及就是模板的定義和實現的全部代碼。所以如果只有.h的話,實例化的時候僅僅只是看到模板的聲明,並不能看到模板的實現,所以需要使用.hpp來實現。把模板的聲明和實現放在一起。
也就是說在任何需要實例化的地方,都需要看看到模板的全部的聲明和定義。