c++模板類和類模板 C++ 模板類



文章插圖
c++模板類和類模板 C++ 模板類

文章插圖
例子1 復數類—-所有函數寫的類的內部
#include<iostream>using namespace std;template<typename T>class Complex{firend ostream & operator<<(ostream &out, Complex &c3){cout << "a:" << " + " << "b:" << c3.b << endl;return out;}public:Complex(T a=0,T b=0);{this->a = a;this->b = b;}Complexoperator+(Complex &c2){Complex tmp(a + c2.a, b + c2.b);return tmp;}void printComplex(){cout << "a:" << a << "b:" << b << endl;}private:Ta;Tb;};//運算符重載的正規寫法,重載<<>>只能用友員函數,其他運算符重載,都要寫成成員函數,不要濫用友員函數void main(){//需要把模板類進行具體化以后,才能定義對象,c++編譯器要分配內存Complex<int>c1(1,2);Complex <int>c2(3, 4);Complex<int>c3=c1+c2;cout << c3 << endl;system("pause");return;}例子2 復數類—-所有函數寫的類的外部,但在一個cpp里
#include<iostream>using namespace std;template<typename T>class Complex{firend ComplexMySub<<(Complex &c1, Complex &c2){Complex tmp(a + c2.a, b + c2.b);return tmp;}firend ostream & operator<< <T> (ostream &out, Complex &c3);public:Complex(T a, T b)Complexoperator+(Complex &c2)void printComplex();private:Ta;Tb;};template<typename T>//構造函數的實現,寫在了類的外部Complex<T>::Complex(T a,T b){this->a = a;this->b = b;}template<typename T>voidComplex::printComplex(){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)//友員函數實現運算符重載{cout << "a:" << " + " << "b:" << c3.b << endl;return out;}void main(){Complex<int>c1(1, 2);Complex <int>c2(3, 4);Complex<int>c3 = c1 + c2;cout << c3 << endl;system("pause");return;}歸納以上的介紹,可以這樣使用.聲明類模板
1)先寫出一個實際的類.由于其語義明確,含義清楚,一般不會出錯.
2)將此類中準備改變的類型名(如int 要改為char)改用一個自己指定的虛擬類型名字
3)在類聲明前加入一行,格式為:
template<class 虛擬類型函數>//注意本行末尾無分號
4)用類模板定義對象使用以下形式:
類模板名<實際類型名>對象名
類模板名<實際類型名>對象名(實參列表)
如:
Compare<int> cmp;
Compare<int> cmp(3,4);
5)如果在類模板外定義成員函數,應該寫成類模板形式:
template<class 虛擬類型函數>
函數類型 類模板名<虛擬函數參數>::成員函數名(參數形參列表)(……)
關于類模板的幾點說明
1)類模板的類型參數可以有一個或多個,每個類型前面都必須加class,
如:
template<class T1,class T2>
class someclass
{……}
定義對象時分別帶入實際的類型名,如;
someclass<int,double>obj;
2)和使用類一樣,使用類模板時要注意其作用域,只能在其有效作用域內用它定義對象.
3)模板可以有層次,一個類模板可以作為基類,派生出派生類的模板,有關這方面的知識實際應用比較少,感興趣的可以自行查閱.
類模板中的ststic關鍵字
從類模板比例實例化的每個模板類都有自己的類模板數據成員,該模板類的所有對象共享一個
ststic數據成員.
和非模板類的ststic數據成員一樣,模板類的ststic數據成員,也應該在文件范圍定義和初始化.
每個模板類都有自己的類模板和ststic數據成員副本.
例子
#include<iostream>using namespace std;template<typename T>class AA{public:static T m_a;private:};class AA1{public:static int m_a;private:};template<typename T>intAA1::m_a = 0;class AA2{public:static char m_a;private:};charAA2::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_asystem("pause");return;}異常問題
一、為什么要有異?!猈HY?
1.通過返回值表達錯誤
像malloc會返回0或1.
局部對象都能正確的析構
層層判斷返回值,流程繁瑣
例子:
#include <iostream>#include <cstdio>using namespace std;int func3 (void) {FILE* fp = fopen ("none", "r");//fopen失敗會返回控指針NULL 。if (! fp)return -1;// ...fclose (fp);return 0;}int func2 (void) {if (func3 () == -1)return -1;// ...return 0;}int func1 (void) {if (func2 () == -1)return -1;// ...return 0;}int main (void) {//層層判斷返回值if (func1 () == -1) {cout << "執行失?。「奶煸僖?!" << endl;return -1;}// ...cout << "執行成功!恭喜恭喜!" << endl;return 0;}2.通過setjmp/longjmp遠程跳轉
一步到位進入錯誤處理,流程簡單
局部對象會失去被析構的機會
例子:
#include <iostream>#include <cstdio>#include <csetjmp> //標c的函數,跳轉using namespace std;jmp_buf g_env; //jmp是專門為c量身定造的,有類的情況不適用,會跳轉,因為不執行右括號,局部對象失去執行析構的機會,不會調用析構函數,會造成內存泄露class A {public:A (void) {cout << "A構造" << endl;}~A (void) {cout << "A析構" << endl;}};void func3 (void) {A a;FILE* fp = fopen ("none", "r");if (! fp)longjmp (g_env, -1); //(沒有定義類的時候)這個時候是的g_env變為-1,但是不在這返回,在main函數的setjmp處返回// ...fclose (fp);}void func2 (void) {A a;func3 ();// ...}void func1 (void) {A a;func2 ();// ...}int main (void) {if (setjmp (g_env) == -1) { //(沒有定義類的時候)第一次到這,genv是0,所以執行下面的func1(),執行了后在fun3中的longjmp處在緩沖區使得g_env變為1,并在這使g_env返回cout << "執行失敗!改天再見!" << endl;return -1;}func1 ();// ...cout << "執行成功!恭喜恭喜!" << endl;return 0;}———————————————————————
3.異常處理
局部對象都能正確的析構
一步到位進入錯誤處理,流程簡單
———————————————————————
二、異常的語法——WHAT?
1.異常的拋出
throw 異常對象;
異常對象可以是基本類型的變量,也可以是類類型的對象 。
當程序執行錯誤分支時拋出異常 。
2.異常的捕獲
try {
可能拋出異常的語句塊;
}
catch (異常類型1 異常對象1) {
處理異常類型1的語句塊;
}
catch (異常類型2 異常對象2) {
處理異常類型2的語句塊;
}

catch (…) {
處理其它類型異常的語句塊;
}
異常處理的流程,始終沿著函數調用的逆序,依次執行右花括號,直到try的右花括號,保證所有的局部對象都能被正確地析構,然會根據異常對象的類型,匹配相應的catch分支,進行有針對性的錯誤處理 。
【c++模板類和類模板 C++ 模板類】例子:#include <iostream>#include <cstdio>using namespace std;class A {public:A (void) {cout << "A構造" << endl;}~A (void) {cout << "A析構" << endl;}};void func3 (void) {A a;FILE* fp = fopen ("none", "r");if (! fp) { //如果不發生異常,不執行throw,直接執行throw后面的語句cout << "throw前" << endl;throw -1; //如果有異常,throw之后的語句不執行,直接右括號cout << "throw后" << endl;}cout << "文件打開成功!" << endl;// ...fclose (fp);}void func2 (void) {A a;cout << "func3()前" << endl;func3 (); //如果有異常,則直接右括號cout << "func3()后" << endl;// ...}void func1 (void) {A a;cout << "func2()前" << endl;func2 (); //有異常,直接右括號cout << "func2()后" << endl;// ...}int main (void) {try {cout << "func1()前" << endl;func1 (); //之后進入func1,先創建a,執行構造,再進入func2,又創建a,執行構造,再進入func3,又創建a,執行構造,然后執行throw,拋出-1;結束func3,釋放func3中的a,調用析構,然后func2結束,釋放func2的a,調用析構,然后func1結束,釋放func1的a,調用析構 。然后直接到try的右花括號,然后執行異常處理,根據異常對象的類型匹配相應的catch,這里是“執行失敗” 。cout << "func1()后" << endl;}catch (int ex) {if (ex == -1) {cout << "執行失?。「奶煸僖?!" << endl;return -1;}}// ...cout << "執行成功!恭喜恭喜!" << endl;return 0;}