第3章 面向對象程序設計_第1頁
第3章 面向對象程序設計_第2頁
第3章 面向對象程序設計_第3頁
第3章 面向對象程序設計_第4頁
第3章 面向對象程序設計_第5頁
已閱讀5頁,還剩63頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

3

章面向對象程序設計【學習目標】

隨著信息技術的發展和企業結構的調整,使用傳統的面向過程語言開發、管理、維護應用軟件變得越來越復雜,越來越困難。面向對象的程序設計技術解決了上述問題。在面向對象程序設計語言里,將數據和處理數據的方法緊密的結合在一起,形成類,將類實例化,就形成了對象。在本章中將向讀者介紹C++面向對象的相關知識。通過學習本章,讀者可以達到以下學習目的:

l

理解對象和類的概念

l

定義并實例化類對象

l

理解類的繼承

l

掌握類的靜態數據成員和靜態方法

l

掌握運算符重載技術課件制作人:宋坤【學習導航】

C++語言被認為是C的超集,對C語言的擴展主要體現在面向對象的技術上。本章主要介紹了C++語言中涉及面向對象的基本知識,包括類和對象的定義,類成員的訪問,構造函數和析構函數的作用,類的繼承,靜態數據成員和靜態方法,運算符重載等。這些內容是面向對象程序設計最基本的知識,希望讀者能夠熟練掌握。本章在書中的學習位置如圖3-1所示。課件制作人:宋坤【知識框架】

本章學習內容知識框架如圖3-2所示。課件制作人:宋坤√本章目錄3.1類和對象3.2類的繼承3.3類的高級方法3.4類模板3.5異常處理3.6課后練習√√√√√課件制作人:宋坤3.1

類和對象

面向對象最大的特征就是提出了類和對象的概念。在以面向對象的方式開發應用程序時,將遇到的各種事物抽象為類,類中包含數據和操作數據的方法,用戶通過實例化類對象來訪問類中的數據和方法。本節將介紹有關類和對象的相關知識。

深入理解類和對象類的定義類的實例化——對象類成員的訪問類成員的保護構造函數析構函數this指針√√√√√√√√課件制作人:宋坤深入理解類和對象

世界中的各種事物,如花、鳥、魚、蟲,都有自己的特征。花具有美麗的色彩,鳥具有一對翅膀……這些事物也都有自己的行為,花能夠生長,鳥能夠鳴叫,魚能夠游動……在面向對象程序設計中,將事物的特征和行為組織在一起,便形成了類。在類中,事物的特征被描述為數據成員,行為被描述為方法。

課件制作人:宋坤類的定義

聲明一個類,需要使用關鍵字class,其后是類名,類名后是一個“{”號,而后是數據成員和方法,結尾是“}”號和分號。下面聲明一個CDog類,包含兩個數據成員和一個方法。classCDog{//數據成員

unsignedintm_Age;unsignedintm_Weight;//方法

voidyelp();};在聲明CDog時,并沒有為其分配內存。它只是告訴編譯器CDog包含哪些數據成員和方法,CDog占用多少內存空間。課件制作人:宋坤類的實例化——對象

對象是類的實例化。在聲明一個類時,并沒有為其分配內存空間,只有在實例化一個對象時,才為對象分配空間。聲明一個對象和聲明一個整型變量類似。下面聲明了一個整型變量i和一個CDog類對象mydog。inti;CDogmydog; //聲明一個對象課件制作人:宋坤類成員的訪問

在定義一個類的對象之后,就可以通過對象訪問類的成員了。在對象之后使用“.”運算符訪問類成員。下面的代碼演示了如何設置mydog的a_Age成員變量,如何調用mydog的yelp方法。mydog.m_Age=2;mydog.yelp();課件制作人:宋坤類成員的保護

在類的聲明時,類的成員(數據和方法)具有安全級別。常用的安全級別有public、private、protected。默認情況下,類的成員為私有的(private),私有成員只能在類本身的方法內訪問,類的對象不能夠訪問私有成員,并且私有成員不能夠被派生類繼承。共有成員(public)能夠被類的所有對象訪問,能夠被派生類繼承。保護成員(protected)不能夠被對象訪問,但能夠被派生類繼承。在類外部不能訪問私有成員和保護成員,只能訪問共有成員。下面的代碼定義了一個CDog類,在該類中定義一個私有成員變量m_Age和一個受保護的成員變量m_Weight,在main函數中訪問CDog對象的m_Age和m_Weight成員時,將出現編譯錯誤。classCDog{//私有成員

unsignedintm_Age;protected://保護成員

unsignedintm_Weight;};

intmain(intargc,char*argv[]){CDogmydog;mydog.m_Age=2; //錯誤,不能訪問私有成員

mydog.m_Weight=10; //錯誤,不能訪問保護成員

return0;}課件制作人:宋坤構造函數

在建立一個對象時,常常需要對對象進行初始化,為了進行初始化工作,C++提供了一個特殊的成員函數——構造函數(constructor)。構造函數是一個與類名相同的方法,可以根據需要設置參數,但不具有返回值,甚至空值也不行。如果在聲明類時,沒有提供構造函數,編譯器會提供一個默認的構造函數,默認構造函數沒有參數,不進行任何操作。在開發程序時,用戶可以自己定義一個默認的構造函數,只要構造函數沒有任何參數就可以了。下面的代碼定義了一個CDog類,在該類中定義一個默認的構造函數,用于初始化成員變量。classCDog{public:unsignedintm_Weight;unsignedintm_Age;CDog();//默認構造函數

~CDog();};

CDog::CDog(){m_Age=1;m_Weight=2;printf("constructinstance\n");}CDog::~CDog(){

}intmain(intargc,char*argv[]){CDogmydog;//調用默認的構造函數

printf("%d",mydog.m_Age);//mydog.m_Age=1return0;}課件制作人:宋坤構造函數編譯器除了提供默認的構造函數外,還提供了默認的復制構造函數。在一個函數中,按值傳遞一個對象或是將對象作為函數的返回值,均會調用類的復制構造函數。所有的復制構造函數均只有一個參數,即該類的對象的引用。因為復制構造函數的目的是生成一個對象的拷貝,所以參數是類的對象的常量引用,即在復制構造函數中不允許修改參數。下面的代碼聲明了一個CDog類的復制構造函數。CDog(constCDog&theDog)當按值傳遞一個對象作為函數參數或者函數返回一個對象時,編譯器將調用復制構造函數重新構造一個對象。下面的代碼演示了復制構造函數的調用。在CDog類中定義一個復制構造函數,并在函數中輸出一個語句。課件制作人:宋坤構造函數classCDog{public:unsignedintm_Weight;unsignedintm_Age;CDog(intweight,intage);CDog(intweight=20);CDog(constCDog&theDog);voidSetAge(unsignedintage);intGetAge();~CDog();};CDog::CDog(intweight,intage){m_Weight=weight;m_Age=age;}CDog::CDog(intweight){m_Weight=weight;}CDog::CDog(constCDog&theDog){m_Weight=theDog.m_Weight;m_Age=theDog.m_Age;printf("copyconstructorfunctionwasinvoked\n");}課件制作人:宋坤構造函數voidCDog::SetAge(unsignedintage){m_Age=age;}

intCDog::GetAge(){returnm_Age;

}

CDog::~CDog(){}

CDogCopyData(CDogm_dog){returnm_dog;}

voidtest(){CDogcustomdog(3,20);//調用自定義的構造函數

CopyData(customdog);}intmain(intargc,char*argv[]){test();return0;}//運行結果copyconstructorfunctionwasinvokedcopyconstructorfunctionwasinvoked課件制作人:宋坤構造函數在上面的代碼中,函數test調用了CopyData函數,該函數是按值傳遞的,因此在調用CopyData函數時,會執行復制構造函數,將實際參數復制一份傳給形式參數,輸出第1行字符串。那么,第2行字符串是如何輸出的?不要忘記,CopyData函數需要一個CDog類型的返回值,在函數返回CDog類型的對象時,會調用一次復制構造函數,將對象返回給被調用函數,輸出第2條語句。總的來說,構造函數的定義和其他方法的定義相同,但需要注意以下幾點。(1)構造函數不能指定返回類型和返回值。(2)一個類可以有多個構造函數,如果沒有定義構造函數,編譯器會自動為類創建一個默認的構造函數。(3)構造函數可以沒有參數,也可以有多個參數,多個構造函數之間就是靠參數的個數和類型來區分的。

課件制作人:宋坤析構函數析構函數是與構造函數是相對的,它是在對象被撤銷后清除并釋放所分配的內存。析構函數與類同名,只是前面有一個“~”號。析構函數沒有返回值,也沒有參數。例如,CDog類的析構函數聲明:~CDog();如果對象是在棧中被創建的,那么在對象失去作用域時,系統會自動調用其析構函數來釋放對象占用的內存。下面根據一個例子來說明析構函數的調用。例如:下面定義一個CDog類,在該類中定義了一個析構函數,用于在CDog對象被釋放時,輸出一條語句。程序代碼如下:課件制作人:宋坤析構函數classCDog{

public:unsignedintm_Weight;unsignedintm_Age;

CDog(intweight,intage);CDog(intweight=20);

CDog(constCDog&theDog);

voidSetAge(unsignedintage);intGetAge();~CDog();};

CDog::CDog(intweight,intage){m_Weight=weight;m_Age=age;}

CDog::CDog(intweight){m_Weight=weight;}CDog::CDog(constCDog&theDog){m_Weight=theDog.m_Weight;m_Age=theDog.m_Age;}

課件制作人:宋坤析構函數voidCDog::SetAge(unsignedintage){m_Age=age;}intCDog::GetAge(){returnm_Age;}CDog::~CDog(){printf("destructorfunctionwasinvoked\n");}voidtest(){CDogmydog;

//調用默認的構造函數

CDogcustomdog(3,20);

//調用自定義的構造函數

}intmain(intargc,char*argv[]){test();return0;}運行結果:destructorfunctionwasinvokeddestructorfunctionwasinvoked在test函數中,本教材聲明了兩個CDog對象,在test函數結束后,系統會自動調用CDog類的析構函數釋放對象,因此輸出了兩行字符串。

課件制作人:宋坤this指針對于類的非靜態成員,每一個對象都有自己的一份拷貝,即每個對象都有自己的數據成員和成員函數。例如:classCBook //定義一個CBook類{public: intm_Pages; //定義一個數據成員

voidOutputPages() //定義一個成員函數 {

printf("%d\n",m_Pages); //輸出信息 }};intmain(intargc,char*argv[]){ CBookvbBook,vcBook; //定義2個CBook類對象

vbBook.m_Pages=512; //設置vbBook對象的成員數據

vcBook.m_Pages=570; //設置vcBook對象的成員數據

vbBook.OutputPages(); //調用OutputPages方法輸出vbBook對象的數據成員

vcBook.OutputPages(); //調用OutputPages方法輸出vcBook對象的數據成員

return0;}執行上述代碼,效果如圖3-1所示。

課件制作人:宋坤this指針從圖3-1中可以發現,vbBook和vcBook兩個對象均有自己的數據成員m_Pages,在調用OutputPages成員函數時輸出的均是自己的數據成員。在OutputPages成員函數中只是訪問了m_Pages數據成員,沒個對象在調用OutputPages方法時是如何區分自己的數據成員呢?答案是通過this指針。在每個類的成員函數(非靜態成員函數)中都隱含包含一個this指針,指向被調用對象的指針,其類型為當前類類型的指針類型,在const方法中,為當前類類型的const指針類型。當vbBook對象調用OutputPages成員函數時,this指針指向vbBook對象,當vcBook對象調用OutputPages成員函數時,this指針指向vcBook對象。在OutputPages成員函數中,用戶可以顯式地使用this指針訪問數據成員。例如:voidOutputPages(){ printf("%d\n",this->m_Pages); //使用this指針訪問數據成員}實際上,編譯器為了實現this指針,在成員函數中自動添加了this指針對數據成員的訪問,類似于上面的OutputPages方法。此外,為了將this指針指向當前調用對象,并在成員函數中能夠使用,在每個成員函數中都隱含包含一個this指針作為函數參數,并在函數調用時將對象自身的地址隱含作為實際參數傳遞。例如,以OutputPages成員函數為例,編譯器將其定義為:voidOutputPages(CBook*this) //隱含添加this指針{

printf("%d\n",this->m_Pages);}在對象調用成員函數時,傳遞對象的地址到成員函數中。以“vc.OutputPages();”語句為例,編譯器將其解釋為“vbBook.OutputPages(&vbBook);”。這樣,就使得this指針合法,并能夠在成員函數中使用。課件制作人:宋坤3.2

類的繼承

繼承是一種編程技術,可以從現有類中構造一個新類,通過新類來實現面向對象的程序設計。本節將從淺入深地介紹類的繼承。

單一繼承多重繼承虛繼承√√√課件制作人:宋坤單一繼承

面向對象的最大特點是實現了繼承。開發人員能夠根據已有類派生一個新類,新類繼承了父類所有公有和受保護的數據成員和方法。類的繼承是指派生類可以全部或者部分的繼承基類的特征,同時加入所需要的新特征和功能。簡單來說,派生類可以有選擇的繼承基類的某些數據成員和方法,同時定義一些新的數據成員和方法。新的類繼承了原有類的特性,被稱為原有類的派生類或子類,原有類被稱為新類的基類或父類。而單繼承就是以一個基類派生新類。在派生一個新類時,使用class關鍵字,其后是類名稱、冒號、訪問限定符(public、private、protected)、基類名稱。在C++中,共有3種訪問限定符,如下。訪問限定符public:表示對于基類中的public數據成員和方法,在派生類中仍然是public,對于基類中的protected數據成員和方法,在派生類中仍然是protected。訪問限定符protected:表示對于基類中的public、protected數據成員和方法,在派生類中均為protected。訪問限定符private:表示對于基類中的public、protected數據成員和方法,在派生類中均為private。下面的代碼定義了一個動物類CAnimal,在該類中定義兩個保護的成員變量,并通過定義4個方法來獲取和設置成員變量。課件制作人:宋坤單一繼承

classCAnimal{protected:char*m_Name;//名稱

char*m_Color;//顏色public:char*GetName();char*GetColor();voidSetName(char*name);voidSetColor(char*color);voidMove();CAnimal();~CAnimal();};

char*CAnimal::GetName(){returnm_Name;}

char*CAnimal::GetColor(){returnm_Color;}

voidCAnimal::SetName(char*name){strcpy(m_Name,name);}

課件制作人:宋坤單一繼承voidCAnimal::SetColor(char*color){strcpy(m_Color,color);}

CAnimal::CAnimal(){m_Name=newchar[5];m_Color=newchar[5];}

CAnimal::~CAnimal(){delete[]m_Name;delete[]m_Color;}

voidCAnimal::Move(){printf("Animalismoving\n");}現在,定義一個CDog類,該類繼承于CAnimal類。這樣,CDog類就擁有了CAnimal類的所有非私有的數據成員和方法。

課件制作人:宋坤單一繼承classCDog:publicCAnimal{protected:char*m_Kind;public:char*GetKind();voidSetKind(char*kind);voidyelp();CDog();~CDog();};

voidCDog::yelp(){printf("Dogcanyelp");}CDog::CDog(){m_Kind=newchar[10];}char*CDog::GetKind(){returnm_Kind;}

voidCDog::SetKind(char*kind){strcpy(m_Kind,kind);}CDog::~CDog(){delete[]m_Kind;}課件制作人:宋坤單一繼承下面的代碼演示了CDog類調用基類的Move方法。intmain(intargc,char*argv[]){CDogdog;dog.SetColor("白色");

dog.SetName("狗");

dog.Move();//調用基類的方法

dog.yelp();//調用自身的方法

return0;}//運行結果:AnimalismovingDogcanyelp課件制作人:宋坤多重繼承多繼承是指派生類有多個基類,這個派生類繼承了多個無關基類的特性。語法:class類名稱:訪問限定符1基類1名稱,訪問限定符2基類2名稱,……在多繼承中,派生類繼承了多個基類的特征,每個基類都由一個訪問限定符來控制其成員在派生類的訪問權限。多繼承使程序重用性得到更大的發揮,可以通過已有的多個不同基類來生成需要的新類。在下面的代碼中,首先定義了一個CBird類和一個CFish類,并為每個類提供了相應的方法。然后定義了一個CWaterbird類,該類派生于CBird類和CFish類,那么CWaterbird類對象就可以調用CBird類和CFish類中定義的方法了。課件制作人:宋坤多重繼承classCBird{public:voidfly();voidmove();};

classCFish{public:voidswim();voidmove();};

voidCBird::fly(){printf("Icanfly\n");}

voidCBird::move(){printf("birdcanmove\n");}

voidCFish::swim(){printf("Icanswim\n");}

voidCFish::move(){printf("fishcanmove\n");}課件制作人:宋坤多重繼承{public:voidrun();};

voidCWaterbird::run(){printf("waterbirdcanrun\n");}

intmain(intargc,char*argv[]){CWaterbirdwaterbird;waterbird.fly();//調用CBird的fly方法

waterbird.swim();//調用CFish的swim方法

return0;}在上面的代碼中,CWaterbird派生于CBird和CFish,因此繼承了CBird的fly方法和CFish的swim方法。課件制作人:宋坤虛繼承采用多重繼承會帶來許多問題。例如,CBird和CFish中存在相同的方法move,子類CWaterbird在調用move方法時會調用哪個父類的方法呢?waterbird.move();答案是編譯器會產生錯誤,你必須指明具體調用類的方法。waterbird.CBird::move();waterbird.CFish::move();采用多重繼承還會帶來一個問題。如果CBird和CFish派生于同一個基類CAnimal,那么CWaterbird類中就會有兩個CAnimal拷貝,如果CWaterbird類沒有覆蓋CAnimal中的方法,如何調用CAnimal中的方法呢?如果直接調用CAnimal中的方法,會出現編譯錯誤,編譯器不知道調用的是CBird父類的方法,還是CFish父類的方法。在調用CAnimal中的方法時,需要指定類名稱。

課件制作人:宋坤虛繼承classCAnimal{

public:voidbreath();};voidCAnimal::breath(){printf("animalcanbreath\n");}classCBird:publicCAnimal{public:

voidfly();voidmove();};classCFish:publicCAnimal{public:

voidswim();voidmove();};voidCBird::fly(){printf("Icanfly\n");}課件制作人:宋坤虛繼承voidCBird::move(){printf("birdcanmove\n");}voidCFish::swim(){printf("Icanswim\n");}voidCFish::move(){printf("fishcanmove\n");}classCWaterbird:publicCBird,publicCFish{public:voidrun();};voidCWaterbird::run(){printf("waterbirdcanrun\n");}intmain(intargc,char*argv[]){CWaterbirdwaterbird;

waterbird.CBird::breath();return0;}課件制作人:宋坤虛繼承在上面的代碼中,CWaterbird調用CAnimal中的breath方法,需要明確指定父類,因為CWaterbird的兩個父類CBird、CFish均派生于CAnimal,CWaterbird類對象中存在兩個CAnimal拷貝。許多讀者會問:是否能夠使CWaterbird類對象中只存在一個CAnimal對象呢?這樣,就可以直接調用CAnimal中的方法了。C++中可以采用虛繼承的方式實現。用virtual關鍵字進行的繼承叫虛繼承,當進行虛繼承時,編譯器會通過指針對其進行處理,使其只能產生一個基類子對象,這樣在編譯器就不會產生錯誤。語法:class類名稱:virtual訪問限定符基類名稱在上面代碼中,只要在CBird和CFish的聲明中使用virtual關鍵字就可以了。classCAnimal{public:voidbreath();};voidCAnimal::breath(){printf("animalcanbreath\n");}classCBird:virtualpublicCAnimal{public:CBird(inti);voidfly();voidmove();};

課件制作人:宋坤虛繼承CBird::CBird(inti){

}classCFish:virtualpublicCAnimal{public:voidswim();voidmove();};voidCBird::fly(){printf("Icanfly\n");}voidCBird::move(){printf("birdcanmove\n");}voidCFish::swim(){printf("Icanswim\n");}voidCFish::move(){printf("fishcanmove\n");}課件制作人:宋坤虛繼承classCWaterbird:publicCBird,publicCFish{public:CWaterbird();voidrun();};CWaterbird::CWaterbird():CBird(1){

}

voidCWaterbird::run(){printf("waterbirdcanrun\n");}

intmain(intargc,char*argv[]){CWaterbirdwaterbird;

waterbird.breath();return0;}在上述代碼中,由于采用了虛繼承,CWaterbird類對象中只有一個CAnimal的拷貝,因此可以直接調用CAnimal中的方法,編譯器不會產生歧義。

課件制作人:宋坤3.3類的高級方法

繼承是一種編程技術,可以從現有類中構造一個新類,通過新類來實現面向對象的程序設計。本節將從淺入深地介紹類的繼承。

內聯方法靜態數據成員和靜態方法友元類和友元函數const方法運算符重載√√√√√課件制作人:宋坤內聯方法

在C++中,你可以編寫像內聯函數一樣的內聯方法,只需要在方法返回值前添加inline關鍵字。下面的代碼定義了一個CMan類,在該類中定義了一個內聯方法。classCMan{public:voidrun();};inlinevoidCMan::run(){printf("mancanrun\n");}也可以將方法的定義放在類的聲明中,此時該方法自動成為內聯方法。classCMan{public:voidrun(){printf("mancanrun\n");}//inline方法};方法run自動成為內聯方法。課件制作人:宋坤靜態數據成員和靜態方法

在C++中可以定義靜態成員數據,所謂靜態成員數據是指其數據屬于類的,通過類名就可以訪問成員數據。在聲明類成員數據時,只要在類型前添加static關鍵字,該成員數據就變為靜態成員數據。下面的代碼定義了一個CMan類,在該類中定義了一個整型靜態成員變量。classCMan{public:staticintdata;};如前所述,靜態數據成員不僅可以由對象訪問,也可以由類訪問。下面的代碼演示了如何通過類名設置和訪問靜態成員數據。classCMan{public:staticintdata;};intCMan::data=1;//初始化靜態成員intmain(intargc,char*argv[]){CMan::data=10;printf("%d",CMan::data);

return0;}//運行結果:10課件制作人:宋坤靜態數據成員和靜態方法靜態成員數據被多個對象所共有,無論定義多少個對象,內存中的靜態成員只有一個。因此,當一個對象修改了靜態成員數據,另一個對象在訪問靜態成員數據時,也會發生變化。classCMan{public:staticintdata;};

intCMan::data=1;

intmain(intargc,char*argv[]){CManman1,man2;man1.data=5;

printf("%d",man2.data);printf("\n");

man2.data=10;printf("%d",man1.data);printf("\n");return0;}//運行結果:510在上面的代碼中,當設置man2的data成員時,實際上也是設置man1的data成員,因為data是CMan的靜態成員變量,man1和man2以及所有CMan類的對象都共享一個data成員變量。課件制作人:宋坤靜態數據成員和靜態方法靜態方法與靜態成員數據一樣,可以由類直接調用。聲明靜態方法與聲明普通方法相似,只是在方法返回值類型前添加static關鍵字。下面的代碼定義了一個CMan類,在該類中定義了一個靜態成員變量和一個靜態成員方法。classCMan{public:staticintdata;staticintgetdata();//靜態方法};

intCMan::getdata(){returndata;}

intCMan::data=1;

intmain(intargc,char*argv[]){CManman1;man1.data=5;printf("%d",CMan::getdata());//由類名直接調用靜態方法

printf("\n");return0;}//運行結果:5課件制作人:宋坤友元類和友元函數在開發應用程序時,一個類經常將另一個類的對象作為自己的成員。這樣,在該類中就可以訪問另一個類的公有(public)數據和方法了。但是,有些時候需要訪問另一個類中的私有數據和方法,該如何實現呢?C++中提供了友元類,在類A中將類B聲明為一個友元類,那么在類B中就可以訪問類A中的私有數據和方法了。下面的代碼定義了一個CHand類和一個CMan類。在CHand類中,將CMan聲明為自己的友元類,這樣,在CMan中就可以訪問CHand中的私有數據和方法了。

classCHand{private:intfingernum;voidwrite();public:friendclassCMan;//將CMan作為自己的友元類

CHand();};

classCMan{public:CHandhand;intgetfingernum();voidhandwrite();};課件制作人:宋坤友元類和友元函數intCMan::getfingernum(){returnhand.fingernum;//訪問CHand中的私有數據}voidCMan::handwrite(){hand.write();//調用CHand中的私有方法}CHand::CHand(){fingernum=5;}voidCHand::write(){printf("handcanwrite\n");}intmain(intargc,char*argv[]){CManman;

printf("%d",man.getfingernum());printf("\n");man.handwrite();return0;}課件制作人:宋坤const方法在聲明類的方法時,可以使用const關鍵字。這樣,該方法就不會改變類中的任何數據成員。對于類中的一些只讀方法,應將其聲明為const方法。例如:intgetheight()const;在開發程序時,對于不想改變類成員數據的函數,應將其聲明為const方法,這樣,編譯器會幫助你發現錯誤。在下面的代碼中,定義了一個CMan類,在該類中定義了兩個const方法,getheight與getweight。在getheight方法中修改了對象的成員變量,出現了編譯錯誤。定義const方法實現成員保護classCMan{private: intheight; intage; intweight;public: intgetheight()const; intgetage()const; intgetweight()const; CMan(); ~CMan();};課件制作人:宋坤const方法CMan::CMan(){ height=0; age=0; weight=0;}

CMan::~CMan(){

}

intCMan::getheight()const{

height=168;//錯誤的代碼,在const方法中不能夠修改height成員變量

returnheight;}

intCMan::getage()const{ returnage;}

intCMan::getweight()const{ returnweight;}課件制作人:宋坤運算符重載C++提供了許多內置數據類型,例如整型、實型、字符型等,也提供了相應的運算符(+、-、>、<等),進行數據的數學運算和邏輯運算。是否能夠在類對象間進行這些運算呢?C++提供了運算符重載的功能,在可以在類中重載某個運算符,這樣就可以運算符作用與類對象。運算符重載的格式為:返回值類型

operator運算符

參數列表。例如:intoperator++();重載++運算符實現自加操作

classCMan{private: intheight;public: intgetheight()const; CMan(); ~CMan(); intoperator++();};intCMan::operator++(){ return++height;}CMan::CMan(){ height=0;}CMan::~CMan(){

}課件制作人:宋坤運算符重載intCMan::getheight()const{ returnheight;}intmain(intargc,char*argv[]){ CManman;

inti=++man; printf("%d",i); printf("\n");

i=++man; printf("%d",man.getheight()); return0;}//運行結果:12在開發程序時,經常重載+運算符實現兩個對象的加法運算,并返回結果(對象)。示例:在下面的代碼中,定義了一個CMan類,在該類中重載+運算符,并返回一個CMan對象。這樣,在程序中就可以實現兩個CMan對象相加了。重載+運算符,實現兩個CMan對象的相加

課件制作人:宋坤運算符重載classCMan{private: intheight; intage;public: intgetheight()const; intgetage()const; CMan(); ~CMan(); CManoperator+(CMan&one);};CManCMan::operator+(CMan&one){

CMan*temp=newCMan; temp->age=age+one.getage(); temp->height=height+one.getheight(); return*temp;}CMan::CMan(){ height=100; age=10;}CMan::~CMan(){

}課件制作人:宋坤運算符重載intCMan::getheight()const{ returnheight;}

intmain(intargc,char*argv[]){ CManman1,man2,man3;

man3=man1+man2; printf("%d",man3.getage()); printf("\n"); printf("%d",man3.getheight()); return0;}//運行結果:20200課件制作人:宋坤3.4類模板類模板的定義及應用定義類模板的靜態數據成員√√

函數模板為函數的參數、返回值等提供了動態參數化的機制,使得用戶能夠動態設置函數的參數類型和返回值類型。而類模板能夠為類的數據成員、成員函數的參數、返回值提供動態參數化的機制,使得用戶可以方便地設計出功能更為靈活的類。本節將介紹有關類模板的相關知識。

課件制作人:宋坤類模板的定義及應用

在介紹類模板之前,先來設計一個簡單的單向鏈表。鏈表的功能包括向尾節點添加數據,遍歷鏈表中的節點,在鏈表結束時釋放所有節點。代碼如下:定義單向鏈表

classCNode //定義一個節點類{public: CNode*m_pNext; //定義一個節點指針,指向下一個節點

intm_Data; //定義節點的數據

CNode() //定義節點類的構造函數 {

m_pNext=NULL; //將m_pNext設置為空 }};classCList //定義鏈表類CList類{private: CNode*m_pHeader; //定義頭節點

intm_NodeSum; //節點數量public: CList() //定義鏈表的構造函數 {

m_pHeader=NULL; //初始化m_pHeader m_NodeSum=0; //初始化m_NodeSum }課件制作人:宋坤類模板的定義及應用CNode*MoveTrail() //移動到尾節點 {

CNode*pTmp=m_pHeader; //定義一個臨時節點,將其指向頭節點

for(inti=1;i<m_NodeSum;i++) //遍歷節點 {

pTmp=pTmp->m_pNext; //獲取下一個節點 }

returnpTmp; //返回尾節點 }

voidAddNode(CNode*pNode) //添加節點 {

if(m_NodeSum==0) //判斷鏈表是否為空 {

m_pHeader=pNode;//將節點添加到頭節點中 }

else //鏈表不為空 {

CNode*pTrail=MoveTrail(); //搜索尾節點

pTrail->m_pNext=pNode; //在尾節點處添加節點 }

m_NodeSum++; //使鏈表節點數量加1 }

voidPassList() //遍歷鏈表 {

if(m_NodeSum>0) //判斷鏈表是否為空 {

CNode*pTmp=m_pHeader; //定義一個臨時節點,將其指向頭節點

printf("%4d",pTmp->m_Data); //輸出節點數據

for(inti=1;i<m_NodeSum;i++) //遍歷其他節點 {

pTmp=pTmp->m_pNext; //獲取下一個節點

printf("%4d",pTmp->m_Data); //輸出節點數據 } } }課件制作人:宋坤類模板的定義及應用~CList() //定義鏈表析構函數 {

if(m_NodeSum>0) //鏈表不為空 {

CNode*pDelete=m_pHeader; //定義一個臨時節點,指向頭節點

CNode*pTmp=NULL; //定義一個臨時節點

for(inti=0;i<m_NodeSum;i++) //遍歷節點 {

pTmp=pDelete->m_pNext; //獲取下一個節點

deletepDelete; //釋放當前節點

pDelete=pTmp; //將下一個節點設置為當前節點 }

m_NodeSum=0; //將m_NodeSum設置為0

pDelete=NULL; //將pDelete置為空

pTmp=NULL; //將pTmp置為空 }

m_pHeader=NULL;

//將m_pHeader置為空 }};下面定義一個鏈表對象,向其中添加節點,并遍歷鏈表節點。代碼如下:遍歷單向鏈表intmain(intargc,char*argv[]){ CListlist; //定義鏈表對象

for(inti=0;i<5;i++) //利用循環向鏈表中添加5個節點 {

CNode*pNode=newCNode(); //構造節點對象

pNode->m_Data=i; //設置節點數據

list.AddNode(pNode); //添加節點到鏈表 }

list.PassList(); //遍歷節點

printf("\n"); //輸出換行

return0;}課件制作人:宋坤類模板的定義及應用執行上述代碼,效果如圖3-2所示。

分析上述代碼中定義的鏈表類CList,一個最大缺陷就是鏈表不夠靈活,其節點只能是CNode類型。為了讓CList能夠適應各種類型的節點,一個最簡單的方法就是使用類模板。類模板的定義與函數模板類似,以關鍵字template開始,其后是由尖括號<>構成的模板參數。下面重新修改鏈表類CList,以類模板的形式進行改寫。利用類模板設計鏈表類template<classType> //定義類模板classCList //定義CList類{private: Type*m_pHeader; //定義頭節點

intm_NodeSum; //節點數量public: CList() //定義構造函數 {

m_pHeader=NULL; //將m_pHeader置為空

m_NodeSum=0; //將m_NodeSum置為0 }課件制作人:宋坤類模板的定義及應用Type*MoveTrail() /獲取尾節點 {

Type*pTmp=m_pHeader; //定義一個臨時節點,將其指向頭節點

for(inti=1;i<m_NodeSum;i++) //遍歷鏈表 {

pTmp=pTmp->m_pNext; //將下一個節點指向當前節點 }

returnpTmp; //返回尾節點 }

voidAddNode(Type*pNode) //添加節點 {

if(m_NodeSum==0) //判斷鏈表是否為空 {

m_pHeader=pNode; //在頭節點處添加節點 }

else //鏈表不為空 {

Type*pTrail=MoveTrail(); //獲取尾節點

pTrail->m_pNext=pNode; //在尾節點處添加節點 }

m_NodeSum++; //使節點數量加1 }

voidPassList() //遍歷鏈表 {

if(m_NodeSum>0) //判斷鏈表是否為空 {

Type*pTmp=m_pHeader; //定義一個臨時節點,將其指向頭節點

printf("%4d",pTmp->m_Data); //輸出頭節點數據

for(inti=1;i<m_NodeSum;i++) //利用循環訪問節點 {

pTmp=pTmp->m_pNext; //獲取下一個節點

printf("%4d",pTmp->m_Data); //輸出節點數據 } } }課件制作人:宋坤類模板的定義及應用~CList() //定義析構函數 {

if(m_NodeSum>0) //判斷鏈表是否為空 {

Type*pDelete=m_pHeader; //定義一個臨時節點,將其指向頭節點

Type*pTmp=NULL; //定義一個臨時節點

for(inti=0;i<m_NodeSum;i++) //利用循環遍歷所有節點 {

pTmp=pDelete->m_pNext;//將下一個節點指向當前節點

deletepDelete; //釋放當前節點

pDelete=pTmp; //將當前節點指向下一個節點 }

m_NodeSum=0; //設置節點數量為0

pDelete=NULL; //將pDelete置為空

pTmp=NULL; //將pTmp置為空 }

m_pHeader=NULL; //將m_pHeader置為空 }};上述代碼利用類模板對鏈表類CList進行了修改,實際上是在原來鏈表的基礎上將鏈表中出現CNode類型的地方替換為模板參數Type。下面再定義一個節點類CNet,演示模板類CList是如何適應不同的節點類型。演示類模板適應不同的節點類型classCNet //定義一個節點類{public: CNet*m_pNext; //定義一個節點類指針

charm_Data; //定義節點類的數據成員

CNet() //定義構造函數 {

m_pNext=NULL; //將m_pNext置為空 }};

課件制作人:宋坤類模板的定義及應用intmain(intargc,char*argv[]){

CList<CNode>nodelist;

//構造一個類模板實例

for(intn=0;n<5;n++) //利用循環向鏈表中添加節點 {

CNode*pNode=newCNode(); //創建節點對象

pNode->m_Data=n; //設置節點數據

nodelist.AddNode(pNode); //向鏈表中添加節點 }

nodelist.PassList(); //遍歷鏈表

printf("\n"); //輸出換行

CList<CNet>netlist; //構造一個類模板實例

for(inti=0;i<5;i++) //利用循環向鏈表中添加節點 {

CNet*pNode=newCNet(); //創建節點對象

pNode->m_Data=97+i; //設置節點數據

netlist.AddNode(pNode); //向鏈表中添加節點 }

netlist.PassList(); //遍歷鏈表

printf("\n"); //輸出換行

return0;}課件制作人:宋坤類模板的定義及應用執行上述代碼,效果如圖3-3所示。

類模板CList雖然能夠使用不同類型的節點,但是對節點的類型是有一定要求的。第一,節點類必須包含一個指向自身的指針類型成員m_pNext,因為在CList中訪問了m_pNext成員。第二,節點類中必須包含數據成員m_Data,其類型被限制為數字類型或有序類型。實際上m_Data成員可以是任意類型,只是在CList類的PassList方法中,本教材為了演示遍歷鏈表節點,使用了“printf("%4d",pTmp->m_Data);”語句輸出節點數據,導致m_Data只能是數字類型或有序類型。課件制作人:宋坤定義類模板的靜態數據成員在類模板中用戶也可以定義靜態的數據成員,只是類模板中的每個實例都有自己的靜態數據成員,而不是所有的類模板實例共享靜態數據成員。為了說明這一點,本教材對模板類CList進行簡化,向其中添加一個靜態數據成員,并初始化進行靜態數據成員。在類模板中使用靜態數據成員template<classType> //定義一個模板類classCList //定義CList類{private: Type*m_pHeader; //定義頭節點

intm_NodeSum; //節點數量public:

staticintm_ListValue; //定義靜態數據成員

CList() //定義構造函數 {

m_pHeader=NULL; //將m_pHeader置為空

m_NodeSum=0; //將m_NodeSum置為0 }};template<classType>intCList<Type>::m_ListValue=10; //初始化靜態數據成員

課件制作人:宋坤定義類模板的靜態數據成員從圖3-4中可以發現,模板實例nodelist和netlist均有各自的靜態數據成員。但是,對于同一類型的模板實例,其靜態數據成員是共享的。例如:同一類型的模板實例,其靜態數據成員是共享的intmain(intargc,char*argv[]) //主函數{

CList<CNode>

nodelist; //定義模板實例

nodelist.m_ListValue=2008; //設置靜態數據成員

CList<CNode>list; //定義模板實例

list.m_ListValue=88; //設置靜態數據成員

printf("%d\n",nodelist.m_ListValue); //輸出數據成員

printf("%d\n",list.m_ListValue); //輸出數據成員

return0;}執行上述代碼,效果如圖3-5所示。

從圖3-5中可以發現,類模板實例nodelist和list共享靜態數據成員,因為她們的模板類均為CNode。

課件制作人:宋坤定義類模板的靜態數據成員下面定義兩個模板實例,分別設置并訪問各自的靜態數據成員。代碼如下:intmain(intargc,char*argv[]) //主函數{

CList<CNode>nodelist; //實例化類模板

nodelist.m_ListValue=2008; //設置靜態數據成員

CList<CNet>netlist; //實例化類模板

netlist.m_ListValue=88; //設置靜態數據成員

printf("%d\n",nodelist.m_ListValue); //輸出靜態數據成員

printf("%d\n",netlist.m_ListValue); //輸出靜態數據成員

return0;}執行上述代碼,效果如圖3-4所示。課件制作人:宋坤3.5異常處理異常是指程序運行時出現的非正常的情況。例如,訪問指針地址無效、除零錯誤等。為了防止程序因出現異常而中斷,C++語言提供了異常處理語句,使得用戶有機會對出現的異常進行處理,增強程序的健壯性。本節將介紹有關C++語言異常處理的相關知識。異常捕捉語句拋出異?!獭陶n件制作人:宋坤異常捕捉語句在C++語言中,為了處理異常,提供了try和catch語句。try語句和catch語句實際上是一個語句塊,try語句塊包含的是可能產生異常的代碼,catch語句塊包含的是處理異常的代碼。下面編寫一段代碼演示try和catch語句的使用。try //try語句塊

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論