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;
	}
	Complex  operator+(Complex &c2)
	{
		Complex tmp(a + c2.a, b + c2.b);
		return tmp;
	}
	void printComplex()
	{

		cout << "a:" << a << "b:" << b << endl;
     }
	
private:
	T  a;
	T  b;
};
//運算符重載的正規寫法,重載<<  >>只能用友員函數,其他運算符重載,都要寫成成員函數,不要濫用友員函數

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 Complex  MySub<<(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)
		Complex  operator+(Complex &c2)
		void printComplex();

private:
	T  a;
	T  b;
};
template<typename T>//構造函數的實現,寫在了類的外部
Complex<T>::Complex(T a,T b)
{
	this->a = a;
	this->b = b;
}
template<typename T>
void  Complex::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>
int  AA1::m_a = 0;

class AA2
{
public:
	static char m_a;
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
	system("pause");
	return;

}

異常問題

一、為什麼要有異常——WHY?

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分支,進行有針對性的錯誤處理。

例子:
#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;
}

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/225692.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 14:45
下一篇 2024-12-09 14:46

相關推薦

發表回復

登錄後才能評論