第21課 - 類模板 - 上
一.類模板的引入
1.1. 一些類主要用於存儲和組織數據元素
如:數組類,鏈表類,Stack類,Queue類等等
C++中可以將模板的思想應用於類,使得類的可以不關注具體所操作的數據類型,而只關注類所需要實現的功能。
1.2 C++中的類模板
1.2.1 提供一種特殊的以相同的行爲處理不同的類型
1.2.2 在類聲明前使用template進行標識
1.2.3 <typename T>用於說明類中使用的泛指類型
template<typename T>
class Operate {
public:
T add(T a, T b)
{
return a + b;
}
T minus(T a, T b)
{
return a - b;
}
};
、1.3 聲明的泛指類型T可用於聲明成員變量和成員函數
1.4 編譯器對類模板的處理方式和函數模板相同
1.4.1 編譯器從類模板通過具體類型產生不同的類
1.4.2 編譯器在聲明的地方對類模板代碼本身進行編譯
1.4.3 編譯器在使用的地方對參數替換後的代碼進行編譯
二.類模板的應用
2.1 使用具體類型定義對象
Operate<int> op1; /* 對象op1用於處理Int型的加減法 */
Operate<double> op2; /* 對象op1用於處理double型的加減法 */
cout<<op1.add(5,4)<<endl;
cout<<op2.minus(1.5,0.01)<<endl;
2.2 類模板的工程應用
2.2.1 由於類模板的編譯機制不同,所以不能像普通類一樣分開實現後,在使用時只包含頭文件。
在工程實踐上,一般會把類模板的定義直接放到頭文件中!!
只有被調用的類模板成員函數纔會被編譯器生產可執行代碼!!!
2.2.2 類模板工程應用步驟
a. 在模板類外部定義成員函數實現時,需要加上
template<typename T>的聲明
Source Example 2.2.2:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
template<typename T>
class Operate {
public:
T add(T a, T b);
T minus(T a, T b);
};
template<typename T>
T Operate<T> :: add(T a, T b)
{
return a + b;
}
template<typename T>
T Operate<T> :: minus(T a, T b)
{
return a - b;
}
int main(int argc, char** argv) {
Operate<int> op1; /* 對象op1用於處理Int型的加減法 */
Operate<double> op2; /* 對象op1用於處理double型的加減法 */
cout<<op1.add(5,4)<<endl;
cout<<op2.minus(1.5,0.01)<<endl;
return 0;
}
b. 由於類模板的編譯機制不同,所以不能像普通類一樣分開實現後,在使用時只包含頭文件。
Source Example(修改數組類):
/* array.h */
#include <iostream>
#include "array.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
Array<int> ia(10);
for (int i = 0; i < 5; i++)
{
ia[i] = i;
}
return 0;
}
/* array.c */
#include "array.h"
#include <iostream>
template <typename T>
Array<T> :: Array(int length)
{
iLength = length;
if (length > 0)
{
pSpace = new int[length];
}
printf ("pSpace = %08x\n", pSpace);
}
template <typename T>
int Array<T> :: getLength()
{
return iLength;
}
template <typename T>
Array<T> :: Array(const Array& obj)
{
iLength = obj.iLength;
pSpace = new int[iLength];
printf ("pSpace = %08x\n", pSpace);
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
}
template <typename T>
T& Array<T> :: operator[] (int i)
{
return pSpace[i];
}
template <typename T>
Array<T>& Array<T> :: operator= (const Array<T>& obj)
{
delete[] pSpace;
iLength = obj.iLength;
pSpace = new int[iLength];
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
return *this;
}
/* 重載比較操作符 */
template <typename T>
bool Array<T> :: operator== (const Array<T>& obj)
{
bool ret = true;
if (iLength == obj.iLength)
{
for (int i = 0; i < iLength; i++)
{
if (pSpace[i] != obj.pSpace[i])
{
ret = false;
break;
}
}
}
else
ret = false;
return ret;
}
template <typename T>
bool Array<T> :: operator!= (const Array<T>& obj)
{
return !(*this == obj);
}
template <typename T>
Array<T> :: ~Array()
{
delete[] pSpace;
}
/* main.c */
#include <iostream>
#include "array.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
Array<int> ia(10);
/* 鏈接會出錯 */
for (int i = 0; i < 5; i++)
{
ia[i] = i;
}
return 0;
}
問題分析:1. 編譯普通類是, main.cpp包含.h,整個工程會對所有文件進行編譯
執行ia[i] = i + 1;時,首先進行語法檢查,然後進行鏈接。
ia[i]是操作符重載,根據成員函數來實現,對三個文件進行編譯, operate[]的代碼被生成,就鏈接過了。
2. 編譯類模板時,第一次編譯三個文件,進行語法檢查
在第二次編譯(第二次編譯要生成代碼)array.cpp時,發現都是函數模板,因此只檢查語法錯誤,不生成代碼。
編譯ia[i] = i + 1;時,需要鏈接operate[]可執行代碼,因此會鏈接出錯。
因此,在main.cpp中需要#include <array.cpp>,而不是#include <array.h>
也需要在array.cpp中添加 #ifndef _ARRAY_DEF_H_
#define _ARRAY_DEF_H_
#endif _ARRAY_DEF_H_
文件後綴名修改爲.hpp
注意:只有被調用的類模板成員函數纔會被編譯器生產可執行代碼!!
(二次編譯)
真正的數組類:
/* array.h */
#ifndef _ARRAY_H_
#define _ARRAY_H_
template <typename T>
class Array{
private:
int iLength;
T *pSpace;
public:
Array(int length);
Array(const Array& a);
int getLength();
~Array();
T& operator[] (int i);
Array& operator= (const Array& obj);
bool operator== (const Array& obj);
bool operator!= (const Array& obj);
};
#endif /* _ARRAY_H_ */
/* array.hpp */
#ifndef _ARRAY_DEF_H_
#define _ARRAY_DEF_H_
#include "array.h"
#include <iostream>
template <typename T>
Array<T> :: Array(int length)
{
iLength = length;
if (length > 0)
{
pSpace = new T[length];
}
printf ("pSpace = %08x\n", pSpace);
}
template <typename T>
int Array<T> :: getLength()
{
return iLength;
}
template <typename T>
Array<T> :: Array(const Array& obj)
{
iLength = obj.iLength;
pSpace = new int[iLength];
printf ("pSpace = %08x\n", pSpace);
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
}
template <typename T>
T& Array<T> :: operator[] (int i)
{
return pSpace[i];
}
template <typename T>
Array<T>& Array<T> :: operator= (const Array<T>& obj)
{
delete[] pSpace;
iLength = obj.iLength;
pSpace = new int[iLength];
for (int i = 0; i < iLength; i++)
{
pSpace[i] = obj.pSpace[i];
}
return *this;
}
/* 重載比較操作符 */
template <typename T>
bool Array<T> :: operator== (const Array<T>& obj)
{
bool ret = true;
if (iLength == obj.iLength)
{
for (int i = 0; i < iLength; i++)
{
if (pSpace[i] != obj.pSpace[i])
{
ret = false;
break;
}
}
}
else
ret = false;
return ret;
}
template <typename T>
bool Array<T> :: operator!= (const Array<T>& obj)
{
return !(*this == obj);
}
template <typename T>
Array<T> :: ~Array()
{
delete[] pSpace;
}
#endif /* _ARRAY_DEF_H_ */
/* main.cpp */
#include <iostream>
#include "array.hpp"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
int main(int argc, char** argv) {
Array<int> ia(5);
for (int i = 0; i < ia.getLength(); i++)
{
ia[i] = i;
}
for (int i = 0; i < ia.getLength(); i++)
{
cout<<ia[i]<<endl;
}
Array<double> ib(5);
for (int i = 0; i < ia.getLength(); i++)
{
ib[i] = i;
}
for (int i = 0; i < ia.getLength(); i++)
{
cout<<ib[i]<<endl;
}
return 0;
}
三.類模板的特化
3.1 類模板可以被特化,即處理一些無法被處理的類型
Source Example 3.1:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
template <typename T>
class Test {
public:
T test(T v)
{
cout << "T test(T v)"<< endl;
cout << "sizeof(T) = "<< sizeof(T)<<endl;
return v;
}
};
int main(int argc, char** argv) {
Test<int> t1;
cout<<t1.test(4)<<endl;
return 0;
}
輸出結果如下:
T test(T,v)
sizeof(T) = 4
但是,我們想要得到結果
T test(int,4)
sizeof(int) = 4
這時,就對Int類型進行特化
Source Example:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
template<typename T>
class Test {
public:
T test(T v)
{
cout << "T test(T v)"<< endl;
cout << "sizeof(T) = "<< sizeof(T)<<endl;
return v;
}
};
template<>
class Test<int> {
public:
int test(int v)
{
cout << "int test(int v)"<< endl;
cout << "sizeof(int) = "<< sizeof(int)<<endl;
return v;
}
};
/* 可以通過繼承將特化類變爲自己的一個普通的類,這就是特化類的工程意義 */
class Mytest : public Test<int>
{
};
int main(int argc, char** argv) {
/* 編譯器會優先選擇特化類來生成對象 */
//Test<int> t2;
Mytest t1;
cout<<t1.test(4)<<endl;
return 0;
}
四.特化類模板的意義
當類模板在處理某種特定類型有缺陷時,可以通過類模板特化來克服處理這種特定類型帶來的不足!
注意:編譯器優先選擇特化類生成對象