泛型編程(模板)
1.模板綜述
背景
- 有時候許多函數或子程序的邏輯結構是一樣的,只是要處理的數據類型不一樣
- 有時候多個類具有相同邏輯的成員函數和成員變量,只是成員變量的數據類型以及成員函數的參數類型不一樣
- 模板就是解決數據類型不一致造成代碼冗餘的一種機制,本質上就是數據類型參數化,用一種邏輯結構抽象出多種數據類型對應的函數或者類
2.函數模板
2.1基礎語法
示例代碼
#include <iostream>
using namespace std;
template <typename T>//模板說明
T myFunc(T size)//函數實現
{
cout << "size:" <<size<< endl;
return size;
}
int main(void)
{
myFunc(8);//自動推導調用
myFunc<float>((float)12.9);//顯示調用
cout << "Hello!" << endl;
//system("pause");
return 0;
}
模板說明裏面的類屬參數在函數定義裏面一定要使用,普通類型可以不使用
可以使用多個類型參數進行模板說明
#include <iostream>
using namespace std;
template <typename T1, typename T2>//模板說明,兩個類型參數
T2 mySort(T1 * array,T2 length)
{
T2 i = 0, j = 0;
T1 tmp = array[0];
for (i = 0; i < length; i++)
{
for (j = i + 1; j < length; j++)
{
if (array[i] < array[j])
{
tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
return i;
}
template <typename T1,typename T2>//模板說明
T2 printArray(T1 *array,T2 length)//函數實現
{
T2 i = 0;
for (i = 0; i < length; i++)
{
cout << (int)array[i]<<""<< endl;
}
cout << endl;
return i;
}
int main(void)
{
char myArray[10] = {1,3,87,54,98,37,33,63,89,2};
int size = sizeof(myArray) / (*myArray);
mySort(myArray, size);
printArray(myArray, size);
cout << "Hello!" << endl;
//system("pause");
return 0;
}
2.2函數模板遇上函數重載
結論:
函數模板不允許自動類型轉化
普通函數能夠進行自動類型轉換
調用規則
1 函數模板可以像普通函數一樣被重載
2 C++編譯器優先考慮普通函數
3 如果函數模板可以產生一個更好的匹配,那麼選擇模板
4 可以通過空模板實參列表的語法限定編譯器只通過模板匹配
/*
函數模板不允許自動類型轉化
普通函數能夠進行自動類型轉換
*/
/*
1 函數模板可以像普通函數一樣被重載
2 C++編譯器優先考慮普通函數
3 如果函數模板可以產生一個更好的匹配,那麼選擇模板
4 可以通過空模板實參列表的語法限定編譯器只通過模板匹配
*/
#include "iostream"
using namespace std;
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
void main()
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl; //當函數模板和普通函數都符合調用時,優先選擇普通函數
cout<<Max<>(a, b)<<endl; //若顯示使用函數模板,則使用<> 類型列表
cout<<Max(3.0, 4.0)<<endl; //如果 函數模板產生更好的匹配 使用函數模板
cout<<Max(5.0, 6.0, 7.0)<<endl; //重載
cout<<Max('a', 100)<<endl; //調用普通函數 可以隱式類型轉換
system("pause");
return ;
}
2.3函數模板實現機制
編譯器介紹
gcc(GNU C Compiler)編譯器的作者是RichardStallman,也是GNU項目的奠基者。
什麼是gcc:gcc是GNU Compiler Collection的縮寫。最初是作爲C語言的編譯器(GNU C Compiler),現在已經支持多種語言了,如C、C++、Java、Pascal、Ada、COBOL語言等。
- gcc支持多種硬件平臺,甚至對Don Knuth 設計的 MMIX 這類不常見的計算機都提供了完善的支持
gcc主要特徵
1)gcc是一個可移植的編譯器,支持多種硬件平臺
2)gcc不僅僅是個本地編譯器,它還能跨平臺交叉編譯。
3)gcc有多種語言前端,用於解析不同的語言。
4)gcc是按模塊化設計的,可以加入新語言和新CPU架構的支持
5)gcc是自由軟件
gcc編譯過程
- 預處理(Pre-Processing)
- 編譯(Compiling)
- 彙編(Assembling)
- 鏈接(Linking)
Gcc *.c –o 1exe (總的編譯步驟)
Gcc –E 1.c –o 1.i //宏定義 宏展開
Gcc –S 1.i –o 1.s
Gcc –c 1.s –o 1.o
Gcc 1.o –o 1exe
結論:gcc編譯工具是一個工具鏈。。。。
GCC常用編譯選項
選項 | 作用 |
---|---|
-o | 產生目標(.i、.s、.o、可執行文件等) |
-c | 通知gcc取消鏈接步驟,即編譯源碼並在最後生成目標文件 |
-E | 只運行C預編譯器 |
-S | 告訴編譯器產生彙編語言文件後停止編譯,產生的彙編語言文件擴展名爲.s |
-Wall | 使gcc對源文件的代碼有問題的地方發出警告 |
-Idir | 將dir目錄加入搜索頭文件的目錄路徑 |
-Ldir | 將dir目錄加入搜索庫的目錄路徑 |
-llib | 鏈接lib庫 |
-g | 在目標文件中嵌入調試信息,以便gdb之類的調試程序調試 |
GCC常用編譯步驟
1.gcc -E hello.c -o hello.i(預處理)
2.gcc -S hello.i -o hello.s(編譯)
3.gcc -c hello.s -o hello.o(彙編)
4.gcc hello.o -o hello(鏈接)
以上四個步驟,可合成一個步驟
gcc hello.c -o hello(直接編譯鏈接成可執行目標文件)
gcc -c hello.c或gcc -c hello.c -o hello.o(編譯生成可重定位目標文件)
Gcc編譯多個文件
hello_1.h
hello_1.c
main.c
一次性編譯
gcc hello_1.c main.c –o newhello
獨立編譯
gcc -Wall -c main.c -o main.o
gcc -Wall -c hello_1.c -o hello_fn.o
gcc -Wall main.o hello_1.o -o newhello
反彙編觀察
命令: g++ -S 7.cpp -o 7.s
函數模板機制結論
- 編譯器並不是把函數模板處理成能夠處理任意類的函數
- 編譯器從函數模板通過具體類型產生不同的函數
- 編譯器會對函數模板進行兩次編譯
- 在聲明的地方對模板代碼本身進行編譯;
- 在調用的地方對參數替換後的代碼進行編譯。
3.類模板
類模板與函數模板的定義和使用類似,我們已經進行了介紹。 有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同,如下面語句聲明瞭一個類:
類模板用於實現類所需數據的類型參數化
類模板在表示如數組、表、圖等數據結構顯得特別重要,
這些數據結構的表示和算法不受所包含的元素類型的影響
使用類模板聲明對象的時候要顯示指定形式參數的具體類型,以便C++編譯器給對象分配具體的內存
3.1普通類模板的語法
單個類的語法
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(int a = 0)
{
this->a = a;
}
void printA()
{
cout << a << endl;
}
protected:
private:
T a;
};
int main(void)
{
A<int> a1;
a1.printA();
A<int> a2(19);
a2.printA();
cout<<"Hello!"<<endl;
return 0;
}
模板類作函數參數
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(int a = 0)
{
this->a = a;
}
void printA()
{
cout << a << endl;
}
protected:
private:
T a;
};
//類模板 做函數參數
//參數 ,C++編譯器 要求具體的類 所以所 要 A<int> &a
void UseA(A<int> &a)
{
a.printA();
}
int main(void)
{
A<int> a1;
UseA(a1);
A<int> a2(19);
UseA(a2);
cout<<"Hello!"<<endl;
return 0;
}
3.2繼承中的類模板語法
模板類派生時, 需要具體化模板類. C++編譯器需要知道 父類的數據類型具體是什麼樣子的。要知道父類所佔的內存大小是多少。只有數據類型固定下來,才知道如何分配內存。
從模板類派生普通類
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(int a = 0)
{
this->a = a;
}
void printA()
{
cout << a << endl;
}
protected:
T a;
private:
};
class B:public A<int>
{
public:
B(int a = 10, int b = 20) : A<int>(a)
{
this->b = b;
}
void printB()
{
cout << "a:" << a << " b: " << b << endl;
}
protected:
private:
int b;
};
int main(void)
{
B b1(1, 2);
b1.printB();
cout<<"Hello!"<<endl;
return 0;
}
從模板類派生模板類
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(int a = 0)
{
this->a = a;
}
void printA()
{
cout << a << endl;
}
protected:
T a;
private:
};
template <typename T>
class C :public A<T>
{
public:
C(T a, T c) :A<T>(a)
{
this->c = c;
}
void printC()
{
cout << "a:" << a << "c:" << c << endl;
}
private:
T c;
protected:
};
int main(void)
{
C<int> c1(1, 2);
c1.printC();
cout<<"Hello!"<<endl;
return 0;
}
3.2類模板知識體系梳理
類模板函數全部寫在類的內部
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
friend Complex MySub(Complex c1, Complex c2)
{
Complex tmp(c1.a-c2.a,c1.b-c2.b);
return tmp;
}
friend ostream & operator<<(ostream & out, Complex & c)
{
out << c.a << " + " << c.b << "i" << endl;
return out;
}
public:
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
Complex operator+(Complex & c2)
{
Complex tmp(a + c2.a,b + c2.b);
return tmp;
}
void printCom()
{
cout << a << " + " << b << "i" << endl;
}
protected:
private:
T a;
T b;
};
int main(void)
{
//需要把模板類 進行具體化以後 才能定義對象 C++編譯器要分配內存
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//濫用友元函數
{
Complex<int> c4 = MySub(c1, c2);
cout << c4 << endl;
}
cout<<"Hello!"<<endl;
return 0;
}
類模板函數全寫在類的外部,但在同一個cpp中
如果一個模板類具有友元函數,且該友元函數的形參包含模板類對象,則需要進行類模板和友元函數的前置聲明。
需要注意的是:
- 模板類和模板類裏面的友元函數需要進行前置聲明
- 友元函數在前置聲明的時候,函數名後面不指定具體的數據類型,但是在模板類裏面聲明的時候,需要在函數名後面緊跟具體的數據類型,比如
<T>
- 模板類的成員函數在類的外面實現的時候,需要注意參數列表,函數名(類作用域)以及返回類型是否需要強行指定具體的數據類型,以便編譯器確定分配內存
- 友元函數在類的外部實現的時候,不需要管函數名前面的類作用域以及函數名後面的具體數據類型,只需要注意參數列表以及返回值是否需要指定數據類型即可
- 所有函數在類的外部實現的時候,其函數名後面都不用管具體的數據類型。
- 友元函數調用的時候,需要在函數名後面緊跟具體數據類型。言外之意,前置聲明和外部實現的格式一樣,類的內部聲明和調用的時候,函數名後面都要具體的數據類型。
- 以上規則只在VS測試過,在其他編譯器也許存在不一樣的情況
#include <iostream>
using namespace std;
template <typename T>//類的前置聲明
class Complex;
template <typename T>//友元函數的前置聲明
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2);//友元函數前置聲明時不需要在函數名後面指定具體數據類型
template <typename T>
ostream& operator<<(ostream& out, Complex<T>& c);//友元函數前置聲明時不需要在函數名後面指定具體數據類型
template <typename T>
class Complex
{
friend Complex<T> MySub<T>(Complex<T>& c1, Complex<T>& c2); //在模板類裏面聲明友元函數的時候,需要在函數名後面緊跟具體的數據類型,比如<T>
friend ostream & operator<<<T>(ostream & out, Complex<T> & c);//在模板類裏面聲明友元函數的時候,需要在函數名後面緊跟具體的數據類型,比如<T>
/*
friend ostream & operator<<(ostream & out, Complex<T> & c);
如果operator<<後面沒有<T>則會報錯,所以模板類裏面存在友元函數的時候,
除了進行友元函數的前置聲明意外,還需要在模板類裏面進行函數名數據類型具體化,
即指定具體的typename參數類型緊跟在函數名後面。
*/
public:
Complex(T a, T b);
Complex operator+(Complex & c2);
void printCom();
protected:
private:
T a;
T b;
};
/*
模板類的成員函數在類的外面實現的時候,
需要注意參數列表,函數名(類作用域)以及返回類型是否需要強行指定具體的數據類型,
以便編譯器確定分配內存
*/
template <typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> & c2)
{
Complex<T> tmp(a + c2.a, b + c2.b);
return tmp;
}
template <typename T>
void Complex<T>::printCom()
{
cout << a << " + " << b << "i" << endl;
}
/*
友元函數在類的外部實現的時候,
不需要管函數名前面的類作用域以及函數名後面的具體數據類型,
只需要注意參數列表以及返回值是否需要指定數據類型即可
*/
template <typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2)
{
Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
template <typename T>
ostream & operator<<(ostream & out, Complex<T> & c)
{
out << c.a << " + " << c.b << "i" << endl;
return out;
}
int main(void)
{
//需要把模板類 進行具體化以後 才能定義對象 C++編譯器要分配內存
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
//c3.printCom();
cout << c3 << endl;
//濫用友元函數
{
Complex<int> c4 = MySub<int>(c1, c2);//友元函數調用的時候,需要在函數名後面緊跟具體數據類型
cout << c4 << endl;
}
cout<<"Hello!"<<endl;
return 0;
}
結論:不要濫用友元函數,一般友元函數只適用於重載
<<
或者>>
操作符。
類模板函數全寫在類的外部,但在不同的.h和cpp中
由於模板的實現機制在本質上是兩次編譯,所以如果只在主程序裏面包含頭文件(類模板的聲明),編譯器不會自動尋找cpp文件裏面的成員函數和友元函數的函數體。所以會出現找不到某個函數體的錯誤。只能是包含實現函數體的cpp文件,而cpp文件又包含了h文件,所以實質上是包含了類模板的聲明以及類模板函數的實現,故業界都是將這兩部分(.h和.cpp)寫在同一個文件中,叫做hpp文件,只需要在提供的開源庫裏面包含該hpp文件,即可使用類模板。
- 傳統類的頭文件(類模板聲明部分)
#pragma once
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
friend ostream & operator<< <T> (ostream &out, Complex &c3);
public:
Complex(T a, T b);
void printCom();
Complex operator+ (Complex &c2);
private:
T a;
T b;
};
- 傳統類的實現部分(具體類模板函數的實現部分)
#include <iostream>
using namespace std;
#include "complex.h"
//構造函數的實現 寫在了類的外部
template <typename T>
Complex<T>::Complex(T a, T b)
{
this->a = a;
this->b = b;
}
template <typename T>
void Complex<T>::printCom()
{
cout << "a:" << a << " b: " << b << endl;
}
template <typename T>
Complex<T> Complex<T>::operator+ (Complex<T> &c2)
{
Complex tmp(a + c2.a, b + c2.b);
return tmp;
}
template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c3)
{
out << c3.a << " + " << c3.b << "i" << endl;
return out;
}
- 測試程序(包含hpp文件)
#include <iostream>
using namespace std;
#include "complex.cpp"
void main()
{
//需要把模板類 進行具體化以後 才能定義對象 C++編譯器要分配內存
Complex<int> c1(1, 2);
Complex<int> c2(3, 4);
Complex<int> c3 = c1 + c2;
cout << c3 << endl;
cout << "hello..." << endl;
return;
}
3.3類模板中的static關鍵字
類模板—>實例化—>模板類
每一個模板類有自己的類模板數據成員,該模板類的所有對象共享一個static數據成員和非模板類的static數據成員一樣,模板類的static數據成員也應該在文件範圍定義和初始化
每個模板類有自己的類模板的static數據成員副本
/*
編譯器並不是把函數模板處理成能夠處理任意類的函數
編譯器從函數模板通過具體類型產生不同的函數
編譯器會對函數模板進行兩次編譯
在聲明的地方對模板代碼本身進行編譯;在調用的地方對參數替換後的代碼進行編譯。
*/
#include <iostream>
using namespace std;
template <typename T>
class AA
{
public:
static T m_a;
protected:
private:
};
template <typename T>
T AA<T>::m_a = 0;
class AA1
{
public:
static int m_a;
protected:
private:
};
int AA1::m_a = 0;
class AA2
{
public:
static char m_a;
protected:
private:
};
char AA2::m_a = 0;
void main()
{
AA<int> a1, a2, a3;
a1.m_a = 10;
a2.m_a ++;
a3.m_a ++;
cout << AA<int>::m_a << endl;
AA<char> b1, b2, b3;
b1.m_a = 'a';
b2.m_a ++;
b2.m_a ++ ;
cout << AA<char>::m_a << endl;
//m_a 應該是 每一種類型的類 使用自己的m_a
cout<<"hello..."<<endl;
system("pause");
return ;
}
3.4類模板小結
類模板的聲明
- 1.先寫出一個實際的類。由於其語義明確,含義清楚,一般不會出錯。
- 2.將此類中準備改變的類型名(如
int
要改變爲float
或char
)改用一個自己指定的虛擬類型名(如上例中的numtype
)。 - 3.在類聲明前面加入一行,格式爲(class和typename作用一樣):
template <class 虛擬類型參數>
如:
template <class numtype> //注意本行末尾無分號
class Compare
{…}; //類體
- 4.用類模板定義對象時用以下形式:
類模板名<實際類型名> 對象名;
類模板名<實際類型名> 對象名(實參表列);
如:
Compare<int> cmp;
Compare<int> cmp(3,7);
- 5.如果在類模板外定義成員函數,應寫成類模板形式:
template <class 虛擬類型參數>
函數類型 類模板名<虛擬類型參數>::成員函數名(函數形參表列) {…}
- 6.類模板的類型參數可以有一個或多個,每個類型前面都必須加
class
或者typename
,如:
template <class T1,class T2>
class someclass
{…};
在定義對象時分別代入實際的類型名,如:
someclass<int,double> obj;
7.和使用類一樣,使用類模板時要注意其作用域,只能在其有效作用域內用它定義對象。
8.模板可以有層次,一個類模板可以作爲基類,派生出派生模板類。
4.模板在工程中的應用
綜述
1.模板是C++中類型參數化的多態工具,提供函數模板和類模板
2.模板定義從模板說明開始,類屬參數必須在模板實現中至少使用一次
3.同一個類屬參數可以用於多類模板
4.類屬參數可用於函數形參,返回類型以及聲明函數中的變量
5.模板由編譯器根據實際的數據類型實例化,生成實際的可執行代碼,從而得到模板函數和模板類
6.函數模板可以進行重載
7.類模板可以進行派生繼承
工程中用到的容器
所有容器提供的都是值(value)語意,而非引用(reference)語意。容器執行插入元素的操作時,內部實施拷貝
動作。所以STL容器內存儲的元素必須能夠被拷貝
(必須提供拷貝構造函數
)。
案例
設計一個數組模板類( MyVector ),完成對int、char、Teacher類型元素的管理。
- 類模板定義
- 構造函數
- 拷貝構造函數
- 重載操作符
<< [] =
操作符- 從數組模板中進行派生
使用基礎數據類型以及一般的自定義類對象作爲容器元素
- 容器/數組類模板頭文件
#pragma once
#include <iostream>
using namespace std;
template <typename T>
class MyVector
{
public:
MyVector(int size = 0);//構造函數
MyVector(const MyVector & obj);//拷貝構造函數
~MyVector();//析構函數
T & operator[](int index);
MyVector & operator=(MyVector obj);
int getLen();
friend ostream & operator<< <T>(ostream & out, const MyVector<T> & obj);
protected:
private:
int m_len;
T *m_space;
};
- 容器/數組類模板實現文件
#include "myvector.h"
template <typename T>
T & MyVector<T>::operator[](int index)
{
return m_space[index];
}
template <typename T>
MyVector<T>::MyVector(int size = 0)//構造函數
{
m_space = new T[size];
m_len = size;
}
template <typename T>
MyVector<T>::MyVector(const MyVector & obj)//拷貝構造函數
{
m_len = obj.m_len;
m_space = new T[m_len];
for (int i = 0; i < m_len; i++)
{
m_space[i] = obj.m_space[i];
}
}
template <typename T>
MyVector<T>::~MyVector()//析構函數
{
if (m_space != NULL)
{
delete[]m_space;
m_len = 0;
m_space = NULL;
}
}
template <typename T>
MyVector<T> & MyVector<T>::operator=(MyVector<T> obj)
{
/*1.釋放舊內存*/
if (m_space != NULL)
{
delete[]m_space;
m_space = NULL;
m_len = 0;
}
/*2.重新分配內存*/
m_space = new T[obj.m_len];
m_len = obj.m_len;
/*3.拷貝數據*/
for (int i = 0; i < m_len; i++)
{
m_space[i] = obj[i];
}
/*4.返回左值本身*/
return *this;
}
template <typename T>
int MyVector<T>::getLen()
{
return m_len;
}
template <typename T>
ostream & operator<<(ostream & out, const MyVector<T> & obj)
{
for (int i = 0; i < obj.m_len; i++)
{
cout << obj.m_space[i] << " ";
}
cout << endl;
return out;
}
- 容器/數組類測試文件
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;
class Teacher
{
public:
Teacher()
{
age = 22;
strcpy(name, "Goopher");
}
Teacher(int age, char *name)
{
this->age = age;
strcpy(this->name,name);
}
void printTeacher()
{
cout << name << "," << age << endl;
}
protected:
private:
int age;
char name[32];
};
class Test1
{
public:
protected:
private:
int a;
};
class Test2
{
public:
protected:
private:
int a;
static int b;//static成員存儲在全局區,不佔類對象的內存模型
};
class Test3
{
public:
virtual void hello()//虛函數使得類對象具有VPTR指針,多佔用一個指針的大小
{
}
protected:
private:
int a;
static int b;
};
class Test4
{
public:
virtual void hello()
{
}
virtual void hello01() = 0;//多個虛函數只對應一個VPTR指針(一個虛函數表)
protected:
private:
int a;
static int b;
};
class Test5
{
public:
virtual void hello()
{
}
virtual void hello01() = 0;
void printTest()//普通成員函數也不佔據類對象的內存模型
{
}
protected:
private:
int a;
static int b;
};
class Test6
{
public:
virtual void hello()
{
}
//virtual void hello01() = 0;
void printTest()
{
}
void printTest01()//多個普通成員函數也不佔據類對象的內存模型
{
}
protected:
private:
int a;
static int b;
};
int main(void)
{
/*初始化v1容器中的沒一個對象並在初始化的時候逐個打印*/
MyVector<int> v1(10);
for (int i = 0; i < v1.getLen(); i++)
{
v1[i] = i + 1;
cout << v1[i] << " ";
}
cout << endl;
/*初始化v2容器中的沒一個對象並在初始化以後使用重載<<的方式打印*/
MyVector<int> v2 = v1;
for (int i = 0; i < v2.getLen(); i++)
{
v2[i] = i*2 + 1;
}
cout <<v2<< endl;
/*使用類對象設置容器並打印*/
Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");
MyVector<Teacher> v3(4);
v3[0] = t1;
v3[1] = t2;
v3[2] = t3;
v3[3] = t4;
for (int i = 0; i < v3.getLen(); i++)
{
Teacher tmp = v3[i];
tmp.printTeacher();
}
cout<<"Hello!"<<endl;
cout << sizeof(Test1)<<endl;
cout << sizeof(Test2) << endl;
cout << sizeof(Test3) << endl;
cout << sizeof(Test4) << endl;
cout << sizeof(Test5) << endl;
cout << sizeof(Test6) << endl;
Test6 t6;
cout << sizeof(t6) << endl;
system("pause");
return 0;
}
優化後的Teacher類對象作爲容器存儲元素(類模板頭文件和實現文件不變)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;
//1 優化Teacher類, 屬性變成 char *panme, 購置函數裏面 分配內存
//2 優化Teacher類,析構函數 釋放panme指向的內存空間
//3 優化Teacher類,避免淺拷貝 重載= 重寫拷貝構造函數
//4 優化Teacher類,在Teacher增加 <<
//5 在模板數組類中,存int char Teacher Teacher*(指針類型)
//=====>stl 容器的概念
class Teacher
{
public:
Teacher()
{
age = 22;
name = new char[1];
strcpy(name, "");
}
Teacher(int age, char *name)
{
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name,name);
}
Teacher(const Teacher &obj)
{
age = obj.age;
name = new char[strlen(obj.name) + 1];
strcpy(name,obj.name);
}
~Teacher()
{
if (name != NULL)
{
delete[]name;
name = NULL;
age = 22;
}
}
void printTeacher()
{
cout << name << "," << age << endl;
}
friend ostream & operator<<(ostream & out, Teacher &obj);
Teacher & operator=(const Teacher & obj)
{
if (name != NULL)
{
delete[]name;
name = NULL;
age = 22;
}
name = new char[strlen(obj.name) + 1];
strcpy(name, obj.name);
age = obj.age;
return *this;
}
protected:
private:
int age;
char *name;
};
ostream & operator<<(ostream & out, Teacher &obj)
{
out << obj.name << "," << obj.age << endl;
return out;
}
int main(void)
{
/*使用類對象設置容器並打印*/
Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");
MyVector<Teacher> v3(4);
v3[0] = t1;
v3[1] = t2;
v3[2] = t3;
v3[3] = t4;
for (int i = 0; i < v3.getLen(); i++)
{
Teacher tmp = v3[i];
tmp.printTeacher();
}
cout<<"Hello!"<<endl;
system("pause");
return 0;
}
指針作爲容器元素存儲
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;
//1 優化Teacher類, 屬性變成 char *panme, 購置函數裏面 分配內存
//2 優化Teacher類,析構函數 釋放panme指向的內存空間
//3 優化Teacher類,避免淺拷貝 重載= 重寫拷貝構造函數
//4 優化Teacher類,在Teacher增加 <<
//5 在模板數組類中,存int char Teacher Teacher*(指針類型)
//=====>stl 容器的概念
class Teacher
{
public:
Teacher()
{
age = 22;
name = new char[1];
strcpy(name, "");
}
Teacher(int age, char *name)
{
this->age = age;
this->name = new char[strlen(name) + 1];
strcpy(this->name,name);
}
Teacher(const Teacher &obj)
{
age = obj.age;
name = new char[strlen(obj.name) + 1];
strcpy(name,obj.name);
}
~Teacher()
{
if (name != NULL)
{
delete[]name;
name = NULL;
age = 22;
}
}
void printTeacher()
{
cout << name << "," << age << endl;
}
friend ostream & operator<<(ostream & out, Teacher &obj);
Teacher & operator=(const Teacher & obj)
{
if (name != NULL)
{
delete[]name;
name = NULL;
age = 22;
}
name = new char[strlen(obj.name) + 1];
strcpy(name, obj.name);
age = obj.age;
return *this;
}
protected:
private:
int age;
char *name;
};
ostream & operator<<(ostream & out, Teacher &obj)
{
out << obj.name << "," << obj.age << endl;
return out;
}
int main(void)
{
/*使用類對象設置容器並打印*/
Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");
MyVector<Teacher*> v3(4);
v3[0] = &t1;
v3[1] = &t2;
v3[2] = &t3;
v3[3] = &t4;
for (int i = 0; i < v3.getLen(); i++)
{
Teacher *tmp = v3[i];
tmp->printTeacher();
}
cout << t1;
cout<<"Hello!"<<endl;
system("pause");
return 0;
}
總結:類模板實現了數據結構(具體數據類型)和算法的分離,真正的實現了泛型編程。