




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C++面向對象程式設計
第一章第1章 C++概述C++ABetterC1.1C++起源和特點 1.1.1 C++的起源
1.1.2 C++的特點1.2C++程式的結構1.2.1
C程式與C++程式比較1.2.2
C++程式結構1.2.3
C++程式的編輯、編譯和運行C程式與C++程式比較之一main(){inta,b,sum;/*定義三個整型變數*/a=123;b=456;
sum=a+b;
printf("sumis%d\n",sum);
}main(){inta,b,sum;
//定義三個整型變數
a=123;b=456;
sum=a+b;
cout<<sum;
}C程式與C++程式比較之二#include"stdio.h"main(){chara,b,c;a='B';b='O';c='Y';putchar(a);putchar(b);putchar(c);}#include“iostream.h"voidmain(){chara,b,c;a='B';b='O';c='Y';cout<<a<<b<<c;}1.3C++的一些新特性1.3.1單行注釋和新的I/O流1.3.2const修飾符1.3.3內聯函數1.3.4函數原型1.3.5帶缺省參數的函數1.3.6函數名重載1.3.7
new和delete運算符1.3.8引用(reference)1.3.1單行注釋和新的I/O流//I/Ostream#include<iostream.h>
main(){inti;floatf;chars[80];
cout<<"Enteraninteger,float,andstring:";cin>>i>>f>>s;cout<<"Here'syourdata:"<<i<<''<<f<<endl<<s<<'\n';return0;}單行注釋和新的I/O流(續)cout是預定義的輸出流對象,類似於C語言中的stdout。<<輸出運算符,可用於輸出C++語言中任何基本類型的數據。cin是預定義的輸入流對象,類似於C語言中的stdin。<<輸入運算符,可用於輸入C++語言中任何基本類型的數據。(注意:輸入和輸出並不是C++語言的組成部分,它們由流庫iostream支持。)輸入含有空格的字串//Usegetline()toreadastringthatcontainsspaces.#include<iostream>#include<fstream>usingnamespacestd;intmain(){charstr[80];cout<<"Enteryourname:";cin.getline(str,79);cout<<str<<'\n';return0;}1.3.2const存取修飾符
對象A:親愛的,你千萬不能變心?對象B:放心吧!親愛的。對象A:你發誓!對象B:不用發誓,因為我是const!
const對象B;常量Constants在C中,可以使用#define來定義符號常量。C++提供了一種更靈活、更安全的方式來定義常量,即使用關鍵字const來定義符號常量。常量例子ConstantexamplesconstfloatPI=3.1415926;//PI是一個常量constintv[]={1,2,3,4};//數組元素v[i]是常量constintx;//error:noinitializer//定義常量時應初始化,否則出錯。voidf(){ model=200;//error v[2]++;//error}值替換valuesubstitution#defineBUFSIZE100constintbufsize=100;Becauseofsubtlebugsthatthepreprocessormightintroduce,youshouldalwaysuseconstinsteadof#definevaluesubstitution.常量const和指針指針所指向的對象為常量Pointertoconst指針本身為常量constpointerTheconstmodifiesthethingitis“closestto.”指向常量的指針
Pointertoconstconstint*u;//pointertoconstant*u=18;//error:upointstoconstantu=p;//OK常指針constpointerintd=1;int*constw=&d;*w=2;//OKw=p;//error:wisconstconst修飾函數參數voidprint_salary(constfloat*salary){ cout<<salary<<’\n’;}const可以阻止參數被函數修改const的其他用途
Otherusesofconstreturntypes,classobjectsandmemberfunctionsvolatile存取修飾符修飾符volatile通知編譯器,變數值可能由程式中沒有顯示說明的方式所改變,因此在作編譯優化時,不要通過將該變數放入寄存器來提高速度。
Volatile原意是:可變的,不穩定的externvolatileclock;externconstvolatileclock;1.3.3內聯函數(內嵌函數,
內置函數inlinefunction)在函數聲明或定義的前面加上關鍵字“inline”,該函數就被聲明為內聯函數。inlineintmax(intx,inty)//max被說明為內聯函數{intz;if(x>y)z=x;elsez=y;return(z);}內聯函數的調用方法與普通函數沒有區別。類中定義的內聯函數InlinefunctionAnyfunctiondefinedwithinaclassbodyisautomaticallyinline,classDate{ intday,month,year;public: voidinit_date(intdd,intmm,intyy) {day=dd;month=mm;year=yy;}};Hereinit_dateisainlinefunction函數調用時的時間開銷1.函數調用時的時間開銷:保護現場,恢復現場。2.用關鍵字inline說明內嵌函數。編譯器直接用內嵌函數體的編譯代碼插入在函數調用語句處,這一過程稱為函數的嵌入擴展。利用內嵌函數減少了調用普通函數時的壓棧和彈棧操作,從而提高程式的運行速度。3.內嵌函數比帶參數的宏的好處。一般情況下,只有較短的函數才定義為內嵌函數。使用內嵌函數實際上是一種增加空間開銷以減小時間開銷的方法。為什麼使用內聯函數Efficiency
效率
在C程式中,可使用宏macros達到同樣的目的,但是宏是通過預處理來處理的,不進行類型檢查,容易造成難以發現的錯誤。宏macros在類的內部不能使用,宏不能作為類的成員。為什麼使用內聯函數(cont.)為了克服宏的上述缺陷,C++引入了內聯函數。內聯函數具有高效率,而且:進行類型檢查,避免出現類型不匹配的錯誤。可以作為類的成員函數。Toretaintheefficiencyofthepreprocessormacro,buttoaddthesafetyandclassscopingoftruefunctions,C++hastheinlinefunction.Howdoinlinefunctionswork
編譯器處理內聯函數的過程類型檢查Typechecking(Toassuresafety)將函數代碼插入到函數調用處thensubstitutesthefunctionbodyforthefunctioncall這樣函數代碼將佔據更所得存儲空間
TheinlinecodedoesoccupyspaceTheshort,smallandfrequentlycalledfunctionsaresuitableforinlinefunctions.1.3.4函數原型(functionprototype)
什麼是函數原型?描述函數原型的三大要素:函數名參數類型函數返回值類型函數原型的例子:inttranslate(floatx,floaty,floatz);inttranslate(float,float,float);【例1.7】
voidsqr_it();/*functiondeclaration*/intmain(){ intx; x=10; sqr_it(x); printf("Thesquareofxis%d\n",x);return0;}voidsqr_it(int*i){ *i=(*i)*(*i);}運行時出錯【例1.7】本例的C程式能夠成功通過諸如TurboC這樣的C編譯器的檢查,但會在運行階段發生錯誤。該程式運行後的結構顯示如下:Thesquareofxis10Nullpointerassignment使用函數原型執行強類型檢查【例1.8】voidsqr_it(int*i);//函數原型
intmain(){ intx; x=10;
sqr_it(x);
cout<<"Thesquareofxis"<<x<<'\n';return0;}voidsqr_it(int*i){ *i=(*i)*(*i);}typemismatch類型不匹配函數原型的作用C++語言是強類型化語言,任何函數在使用以前必須有該函數的原型說明,以便進行實際參數與形式參數之間的類型匹配檢查。函數返回值的類型和函數參數的類型、個數、次序在函數聲明,函數定義和函數調用時必須匹配。C++語言的編譯器執行上述檢查能顯著減少很多隱藏的錯誤。函數原型與C語言的函數類型說明函數原型是在C語言的函數類型說明(函數聲明)的基礎上進行擴充後形成的,它不但說明了函數返回值的類型,還要確定函數參數的類型、個數、次序及缺省值。1.3.5帶缺省參數的函數例如:以下函數帶有一個缺省值為0的參數。voidmyfunc(doubled=0.0){…}myfunc(198.234);//passanexplicitvaluemyfunc();//letfunctionusedefault缺省參數的例子voidDrawCircle(intx,inty,intr=10);DrawCircle(50,20);DrawCircle(50,100,30);帶缺省參數函數主要由兩個作用:簡化編程;有利於程式擴充,而不影響原有代碼。1.3.6函數名重載(overload)兩或兩個以上的函數共用同一個名稱,就稱為函數名重載。
OverloadedFunctionsMultiplefunctionscanhavethesamenamewithdifferentimplementations.函數重載簡化了函數調用工作。函數重載的例子#include<iostream.h>
//Overloadabs()threeways
intabs(intn);longabs(longn);doubleabs(doublen);//prototypeisneccessaryforC++compiler這些都是函數原型函數重載的例子main(){cout<<"Absolutevalueof-10:"<<abs(-10)<<"\n";cout<<"Absolutevalueof-10L:"<<abs(-10L)<<"\n";cout<<"Absolutevallueof-10.01:"<<abs(-10.01)<<"\n";
return0;}abs()forints//abs()forintsintabs(intn){cout<<"Inintergerabs()\n";returnn<0?-n:n;}abs()forlongs
//abs()forlongslongabs(longn){cout<<"Inlongabs()\n";returnn<0?-n:n;}abs()fordoubles//abs()fordoublesdoubleabs(doublen){cout<<"Indoubleabs()\n";returnn<0?-n:n;}1.3.7
new和delete運算符在C語言中,使用函數malloc()分配動態記憶體,用函數free()釋放動態記憶體;在C++語言中,還可以使用運算符new分配動態記憶體,用delete釋放動態記憶體。
new和delete的簡單應用//Asimpleexampleofnewanddelete.#include<iostream.h>
main(){int*p;
p=newint;//allocateroomforaninteger
//alwaysmakesurethatallocatesucceededif(!p){cout<<"Allocationerror\n";return1;}Asimpleexampleofnewanddelete(cont)*p=1000;
cout<<"Hereisintegeratp:"<<*p<<"\n";
deletep;//releasememory
return0;}new和delete的優點new和delete完成的功能類似於malloc()與free(),但它們有幾個優點:1)簡潔性:能自動計算所要分配的記憶體的大小;2)可靠性:編譯時進行類型檢查;3)靈活性:new和delete運算符可以被重載。注意:用new申請的動態記憶體必須用delete釋放。1.3.8引用(reference)引用是一個隱含指針,可以看作變數的另一個名稱(別名)。引用有三種使用方法:引用作為函數參數(最重要的用法)#include<iostream.h>
voidf(int&n);//declareareferenceparameterusing&
main(){inti=0;
f(i);//Theaddressofvariableiispassed.cout<<"Hereisi'snewvalue:"<<i<<'\n';return0;}引用作為函數參數(繼續)//f()nowusesareferenceparametervoidf(int&n){//noticethatno*isneededinthefollowingstatementn=100;//put100intotheargumentusedtocallf()}指針參數和引用參數的比較//交換實際參數的值voidswap(int*x,int*y){inttemp;
temp=*x;//保存地址x中的值
*x=*y;//putyintox
*y=temp;//putxintoy}
//調用函數swap()時使用變數i和j的地址
swap(&j,&i);//交換實際參數的值voidswap(int&x,int&y){inttemp;
temp=x;//保存地址x中的值
x=y;//putyintox
y=temp;//putxintoy}
//調用函數swap()時使用變數i和j的名字
swap(j,i);引用參數的幾個好處當使用引用參數的時候,傳遞的是用作參數的變數的地址。1)地址被自動傳遞;不需要記住傳遞參數的地址。2)比指針方法更簡練,清晰。3)當對象作為引用被傳遞給函數的時候,沒有進行拷貝(複製)。引用作為函數的返回值
//Asimpleexampleofafunctionreturnnigareference.#include<iostream.h>int&f();intx;
main(){ f()=100;//f()returntheaddressofx
cout<<x<<"\n";
return0;}引用作為函數的返回值(繼續)
//Returnanintreference.int&f(){returnx;//returnsareferencetox}獨立的引用至今沒有發現獨立引用的價值,在一個程式中用兩個名字來描述同一個對象,可能使程式出現混亂。(應當避免)對象和類屬於同一個類的所有對象具有某些共性和相似的特徵。一個類定義了一組大體上相似的對象。在面向對象的軟體系統中,對象是基本的運行時實體,它既包含數據,也包括作用於這些數據的操作。對象對象的組成操作代碼數據面向對象的軟體系統對象4對象1對象3對象2§2.1類和對象的定義2.1.1
C++中對結構的擴展2.1.2
C++中類的定義2.1.3
C++類中的成員函數定義2.1.4
C++中對象的定義和使用2.1.5
C++中類的介面與實現2.1.6類聲明與類定義2.1.7結構struct與類class的比較2.1.1
C++中對結構的擴展C++中的結構不僅可以包含不同類型的數據,而且還可以包含函數。結構中的數據和函數都是結構的成員,分別稱為數據成員和函數成員。在C++中,通常把函數成員稱為成員函數。C的結構體struct與C++的struct的比較C語言的結構體中數據與操作是分離的
在C++語言中將數據與操作封裝在一個結構體中
structStudent{ intnumber; charname[15]; floatscore;};數據成員structStudent{intnumber;charname[15];floatscore;voiddisplay()//函數成員{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};/*獨立函數display*/voiddisplay(Student*stu){ printf(”number:%d”,stu->number); printf(”name:%s”,stu->name); printf(”score:%f\n”,stu->score);}成員函數數據成員2.1.2
C++中類的定義在C++語言中,我們通過定義新的數據類型來實現類。類既包含數據內容又包含對數據的操作,所以類是一個抽象數據類型。在C++語言中,類可被視為一種用戶定義的類型。C++的結構體struct與C++的class的比較將數據與操作封裝在一個C++的結構體struct中將數據與操作封裝在一個C++類class中數據成員structStudent{intnumber;charname[15];floatscore;voiddisplay()//函數成員{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};數據成員classStudent{intnumber;charname[15];floatscore;voiddisplay()//函數成員{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};成員函數數據成員一個複數結構的例子structcomplex{doublereal;doubleimage;voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(){returnreal;}…;};成員函數數據成員私有成員和公有成員在C++中一個結構的成員通常分為兩類:私有成員(private)和公有成員(
public)私有成員只能被該結構中的其他成員訪問。公有成員既可以被結構內的其他成員訪問,也可以被結構外的成員所訪問。C++規定,在缺省情況下,結構中的成員是公有的。此處所指成員包括數據成員和成員函數。私有成員和公有成員的聲明structcomplex{private:doublereal;doubleimage;public:voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(
){returnreal;}…;};私有成員公有成員類的定義class
類名{[private:]
私有數據成員和成員函數public:
公有數據成員和成員函數};//分號是不能少的//其中,class是聲明類的關鍵字//類名是要聲明的類的名字類中私有成員和公有成員的聲明classcomplex{private:doublereal;doubleimage;public:voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(){returnreal;}…;};私有成員公有成員定義類時的注意事項private和public可以按任意順序出現具有良好習慣的程式員會:把所有私有成員和公有成員歸類放在一起將私有成員放在公有成員的前面private、public和protected稱為訪問控制符。數據成員可以是任何數據類型,但不能用auto、register或extern進行說明。只有在類對象定義之後才能給數據成員賦初值。為什麼要用類代替結構?在缺省情況下,類中的成員是私有的private。類提供了缺省的安全性。2.1.3
C++類中的成員函數定義C++類中的成員函數描述了對類對象內部數據的操作方法,或者說,描述了對象的行為,因此我們又將成員函數稱為方法(method)或者服務(service)。【例2.2】學生類中成員函數的定義。成員函數的定義包含在類體中classStudent{private: intnumber; charname[15]; floatscore;public: voidinit(intnumber1,char*name1,floatscore1);
voidmodify(floatscore1) { score=score1; } voidprint();};成員函數在類體外定義voidStudent::init(intnumber1,char*name1,floatscore1){作用域解析運算符
number=number1; strcpy(name,name1); score=score1;}voidStudent::print(){ cout<<”number:”<<number<<”name:”<<name<<”score:”<<score<<’\n’;}成員函數在類體內外的區別一般情況下,在類體中僅給出成員函數的原型,而把成員函數的定義放在類體之外實現。這種將類的成員函數的聲明(declaration)和定義(definition)進行分離的方式有很多好處類體內定義的成員函數在編譯時是以內聯函數處理的,只有那些非常簡短的函數才在類體中直接定義。2.1.4
C++中對象的定義和使用在C++語言中,類是用戶定義的一種新類型,所以可以象聲明普通變數一樣來建立對象。在C++語言中,類與對象的關係就好象整型int和整型變數i之間的關係一樣。注意:所有類對象都共用它們的成員函數,但是,每一個對象都建立並保持自己的數據。對象的數據成員,在C++中稱為實例變數。創建對象的方法之一在定義類時同時創建對象,例如:classdate{intmonth,day,year;public:voidsetdate(int,int,int);voidprint();intgetyear();intgetmonth();intgetday();}tt;//同時創建對象tt。創建對象的方法之二在定義類後在創建對象,例如:Datadate1,date2;對象的使用對象的性質和定義要求將實例變數隱藏在對象中,對它們的訪問,原則上要通過其介面——共有的成員函數來進行。訪問對象的成員時,使用“.”運算符:對象名.成員名稱;調用成員函數是必須指明對象,事實上,在C++語言中,我們用OOP術語發送消息來代替“調用成員函數”這個說法。2.1.5
C++中類的介面與實現一般把僅含函數原型的類聲明部分稱為類的介面(interface),例如:classDate{ intday,month,year;public: voidinit(intdd,intmm,intyy);//initialize voidadd_year(intn); //addnyears voidadd_month(intn);//addnmonths voidadd_day(intn); //addndays}C++中類的介面與實現(續)把類中成員函數的定義部分稱為類的實現部分(implementation)。例如:voidDate::init(intdd,intmm,intyy){ day=dd; month=mm; year=yy;}inlinevoidDate::add_year(intn){ year+=n;}C++中類的介面與實現(續)C++中的類實現了介面和實現的分離,這樣只要維持介面不變,實現部分可以根據需要不斷改進,而不影響類的使用。類的介面部分一般存放在擴展名為h的頭檔中,類的實現部分則存放在擴展名為cpp的實現檔中。在cpp檔的開頭要用include將h頭檔包含在其中。這個特徵增強了類模組的獨立性和可重用性(reuse)。設計一個類(class)時的基本原則在設計一個新的類(class)類型時的基本原則是將類的複雜的實現細節(例如用來存儲該類對象的數據)和正確使用該類所需知道的要素(例如訪問對象數據的函數)分離開來。這種分離的最好方式就是通過一個特定的介面來使用該類的數據結構和內部處理程式。封裝(encapsulation)在進行類定義時,我們把需要提供給類的用戶的在類對象上執行的操作聲明為公用的(public),這樣就形成了類的介面。然後按照資訊隱藏(informationhiding)的原則來把類的內部數據表示和實現細節聲明為私有的(private),從而完成了封裝(encapsulation)。封裝性及其好處所謂封裝,在面向對象程式設計中的含義就是包含和隱藏對象資訊,如內部數據結構和代碼的能力。在C++中封裝是通過類來實現的,即將內部的數據和代碼聲明為私有的,外界不能直接訪問。封裝將操作對象的內部複雜性與應用程式的其他部分隔離開來。換句話說,應用類對象的程式員不需要知道類內部的實現細節就可以使用該類。這是面向對象程式設計降低複雜性的最主要途徑。2.1.6類聲明與類定義類定義包含兩部分:類頭和類體,類體由一對花括弧包圍起來。一旦到了類體的結尾,即結束右邊的花括弧,這時就定義了一個類。一旦定義了一個類,則該類的所有成員就都是已知的,類的大小也是已知的了。類聲明由於歷史的原因,類的定義(classdefinition)也稱為類的聲明(declaration)。但是準確地說,一旦給出了類頭,就聲明了一個類。我們可以聲明一個類但是不定義它。例如:classDate;//Date類的聲明這個聲明向程式引入了一個名字Date,指示Date為一個類類型。類聲明(續)如果還沒有定義類,那麼我們就不能定義這類類型的對象但是,我們可以聲明指向這種類型對象的指針或者引用。classDate; //Date類的聲明Datepd,&rd;//聲明了指向Date類對象的指針pd和引用rd2.1.7結構struct與類class的比較結構和類的唯一區別是:在缺省情況下結構內數據成員和成員函數是公有的public,而類中的數據成員和成員函數是私有的private。可以說C++中的結構是一種特殊的類。類(或者結構)的私有成員只能被該類(或者結構)的成員函數訪問,這是C++實現封裝的一種方法。§2.2構造函數和析構函數2.2.1構造函數2.2.2析構函數2.2.3重載構造函數2.2.4組合類和對象成員的初始化2.2.1構造函數constructor構造函數是類的一種特殊成員函數,用來為對象進行初始化。構造函數的函數名與類名相同。構造函數的例子1classDate{
intday,month,year;
Date(intdd,intmm,intyy);//constructor};//構造函數名與類名相同voidDate::Date(intdd,intmm,intyy){ day=dd; month=mm; year=yy;}用來為對象進行初始化。構造函數的例子2classStudent{private: intnumber; charname[15]; floatscore;public: Student(intnumber1,char*name1,floatscore1);voidmodify(floatscore1) {score=score1;} voidprint();};構造函數的聲明構造函數的例子2//構造函數的定義Student::Student(intnumber1,char*name1,floatscore1){ number=number1; strcpy(name,name1); score=score1;}構造函數(續)構造函數可以有任一類型的參數,但不能具有返回類型。當定義一個對象時,系統自動調用構造函數進行初始化。構造函數與其它函數一樣可以重載也可以有缺省參數。在對象生成的時候自動執行初始化,這會消除任何錯誤地不執行初始化的可能。這是C++面向對象程式設計幫助減少複雜性的另一途徑。有缺省參數的構造函數classcomplex{private:doublereal;doubleimage;public:voidcomplex(doubler=0.0,doublei=0.0){real=r;image=i;}doublerealcomplex(){returnreal;}…;};缺省參數2.2.2析構函數析構函數的作用與構造函數正好相反,當對象被刪除時,利用析構函數進行一些善後處理。一般情況下析構函數執行構造函數的逆操作,例如可以利用析構函數來釋放構造函數所動態申請的記憶體空間。析構函數的名稱與其類名稱相同,並在名稱的前邊加~(鍵盤上的波浪符號)符號。一個類中只能有一個析構函數。(不能重載)析構函數不允許有返回值,並且析構函數不允許帶參數。參見【例2.7】。構造函數和析構函數的例子//構造函數的定義Student::Student(intnumber1,char*name1,floatscore1){ number=number1; name=newchar[strlen(name1)+1];//申請動態記憶體單元
strcpy(name,name1); score=score1;}//析構函數的定義Student::~Student(){
delete[]name;//釋放動態記憶體單元}2.2.3重載構造函數構造函數與其它函數一樣可以重載也可以有缺省參數。要重載類的構造函數,只需要在類中聲明構造函數的不同函數原型並且定義相關的函數體。重載構造函數主要有三個原因
1.為了獲得靈活性:提供給用戶多種初始化對象的選項。2.為了支持數組:要聲明一個類的對象數組則該類應具有一個不帶參數的構造函數。3.為了自定義複製構造函數,將對象進行準確的複製(拷貝):參見第5章堆與複製構造函數。
重載構造函數例一classDate{ intd,m,y;public://... Date(int,int,int);//day,month,year Date(int,int);//day,month,today’syear Date(int);//day,today’smonthandyear Date();//defaultDate:today Date(constchar*);//dateinstringrepresentation};為了獲得靈活性:提供給用戶多種初始化對象的選項2.2.4組合類和對象成員的初始化當一個類的對象作為一個類的成員時,稱為該類的對象成員。使用對象成員著重要注意的問題是一個類的內部對象的初始化問題。類的對象成員初始化#include"iostream.h"classinner_class{ intx;public: inner_class(intz){x=z;} voidwrite(){cout<<x<<endl;}};類的對象成員初始化(續)classouter_class{ inty; inner_classx; inner_classr;public: outer_class(intz); voidwrite(){cout<<y<<endl;} voidwrite_inner_x() {x.inner_class::write();} voidwrite_inner_r() {r.inner_class::write();}};注意:outer_class類對象在通過構造函數進行初始化的同時,也要對其成員對象進行初始化。類的對象成員初始化(續)outer_class::outer_class(intz):x(20),r(-36){y=z;}main(){ outer_classobj(10); obj.write_inner_x(); obj.write_inner_r(); obj.write(); return0;}初始化參數傳遞鏈初始化和撤銷的順序先裏後外:先調用對象成員的構造函數,再調用週邊類的成員函數。雖然對象成員調用它們類的構造函數,但此時寫的是對象的名字,而不是類名。先外後裏:當撤銷一個包含對象成員的對象時,該對象的析構函數將先執行,然後再調用對象成員的析構函數。【例2.9】對象成員的初始化。//注意:在定義Student類的構造函數時,必須綴上對象成員的名字birthdayStudent::Student(intnumber1,char*name1,floatscore1,intd1,intm1,inty1):birthday(d1,m1,y1)//對象成員birthday的初始化{ number=number1; strcpy(name,name1); score=score1;}2.3類與const軟體工程的歷史經驗表明,程式內部相當多的隱蔽錯誤是由於無意中修改了某些數據的值造成的。對數據進行保護是減少程式中的錯誤,提高軟體的可靠性的有效途徑之一。在C++中還廣泛使用const關鍵字,用來實現對共用數據的保護,提高軟體的安全性。2.3.1常成員函數
ConstantMemberFunctionsclassDate{ intd,m,y;public: intday()const{returnd;} intmonth()const{returnm;} intyear()const; //...};使用const關鍵字說明的函數常成員函數不更新對象的數據成員。(只讀函數)常成員函數(續)inlineintDate::year()const//正確{ returny;}inlineintDate::year()//錯誤{ returny;}const是函數類型的一個組成部分,因此在實現部分也要帶const關鍵字。遺漏了const關鍵字常成員函數(續)inlineintDate::year()const{ returny++;}將出現編譯錯誤資訊:企圖在常成員函數中修改對象的數據成員。2.3.2常對象constantobject常對象是指對象常量,定義格式如下:<類名>const<對象名>或者
const<類名><對象名>
例如:constDatecd;常對象constantobject(續)必須對常對象進行初始化,而且不能被更新。常對象只能調用它的常成員函數。普通的對象既可以調用它的常成員函數,也可以調用普通成員函數。請看下麵的例子:常對象constantobject(續)voidf(Date&d,constDate&cd){ inti=d.year();//ok d.add_year(1);//ok intj=cd.year();//ok cd.add_year(1);//錯誤}常對象不能調用它的普通成員函數3.1類與對象3.1.1對象3.1.2類3.1.3對象與類的關係對象的廣義定義什麼是對象(object)?現實世界中的任何一個事物都可以看成是一個對象。自然的實體:一個人,一輛汽車,一個教師邏輯結構:一個銀行帳號,一個學生的學籍檔案,客戶通信錄對象的特性對象是人們要研究的任何事物,其特性是:1、每一個對象必須有一個名字以區別於其他對象;2、用屬性(或叫狀態)來描述它的某些特徵;3、有一組操作,每一個操作決定對象的一種行為。//這是關於對象的廣義定義面向對象的系統中的對象對象是基本的運行時實體,它既包含數據(屬性),也包括作用與數據的操作(行為)。一個對象把屬性和行為封裝成一個整體。對象是數據和對數據的操作的結合體。從程式設計者來看,對象是一個程式模組;從用戶來看,對象為他們提供了所希望的行為。類的例子人類水果類魚類“類”是對一組具有共同屬性特徵和行為特徵的對象的抽象。OOP中類的例子classStudent{ intnumber; char*name; floatscore;public:
Student(intnumber1,char*name1,floatscore1); ~Student(); voidmodify(floatscore1); voidprint();};屬性操作什麼是類(class)?在C++語言中,我們通過定義新的數據類型來構成類。在新的數據類型中,既包含數據內容又包含對數據的操作。一個類所包含的方法和數據描述一組對象的共同行為和屬性。什麼是類(class)?一個類定義了一個大體上相似的對象。把一組對象的共同特性加以抽象並存儲在一個類中的能力,是面向對象技術最重要的一點。對象與類的關係類是對一組性質相同的對象的描述,是對一組數據和方法的封裝。對象則是類的具體化,是類的實例。可以這樣定義對象:對象是類的一個實例,包括了數據和過程。3.2消息和方法3.2.1消息3.2.2方法消息Message消息是要求某個對象執行其中某個功能操作的規格說明。OOP中的一條消息由消息選擇器(“消息操作”或“消息名”)及若干個參數和接受消息的對象三部分組成,例如:student1.modify(score1);消息的例子接受消息的對象參數↓↓student1.modify(score1);↑
消息名發送消息與函數調用的比較1) 函數調用可以帶或不帶參量,但消息至少帶一個參量(對象名或對象指針);它指明接受該消息的對象。消息選擇器則告訴對回應作些什麼。2) 消息名(消息選擇器或消息操作)類似於函數名,但二者之間的本質差別在於:函數名僅代表一段可執行的代碼,而消息名的具體功能的實現取決於所接受消息的對象。發送消息與函數調用的比較(續)3) 函數調用是過程式(面向過程)的即“如何做(Howtodo)”,而消息則是通知式(面向對象)的,即告訴對象“做什麼(Whattodo)”,具體“如何做(Howtodo)”由對象根據接受到的消息自行確定。lst.sort();其中,lst代表一個鏈表對象,sort是表示排序的消息名。方法(method)“方法”對應於對象的行為(能力),即它是實現對象所具有的功能操作的代碼段。在C++程式中,方法即是類中定義的成員函數,它是該類對象所能執行的操作的演算法實現。通常每個類中包含多個方法(即C++的成員函數),每個方法由方法名(函數名+參數表)和說明該成員函數的演算法實現的一段代碼所組成。方法的例子voidStudent::print(){ cout<<”number:”<<number<<”name:”<<name<<”score:”<<score<<’\n’;}方法是與對象相連決定怎麼做的操作執行代碼,所以方法是實現每條消息具體功能的手段。3.3什麼是面向對象程式設計3.3.1結構化程式設計方法3.3.2面向對象程式設計方法表3.1程式設計方法的發展過程1957~1972:面向過程的程式設計1968~1990:面向過程的結構化程式設計1984~今:面向對象的程式設計結構化程式設計的基本思想按功能劃分模組,分而治之。分解系統時按照自頂向下的順序,逐步求精。設計時使各模組間的關係盡可能簡單,功能上相對獨立,從而可單獨驗證模組的正確性。結構化程式的組成
主控模組功能模組1功能模組7功能模組2功能模組3功能模組6功能模組5功能模組4圖3.3結構化程式的組成結構化程式設計方法的基本特點把數據結構和處理數據的操作過程分離為相互獨立的實體。用數據表達實際問題中的資訊;程式代碼則實現用於處理加工這些數據的演算法。簡而言之,就是數據和操作代碼分離。
數據和操作代碼分離產生的問題給程式員增加了負擔:必須確保數據和代碼匹配。當數據結構改變時,所有相關的處理過程都要進行相應的修改。可維護性低。即使要對不同的數據格式(結構和類型)作同樣的處理計算,也必須編寫不同的程式。可重用性不好。面向對象程式設計用面向對象程式設計語言中的對象和類直接模擬現實世界中的對象,將問題空間直接映射到軟體空間。從而設計出盡可能直接、自然地表示問題求解方法的軟體。面向對象的這種思維方式符合人類的自然思維習慣,使我們能夠在程式中自然地描述現實世界的實體和問題,增強了程式代碼的可讀性,有利於控制軟體的複雜性。模組化將一個複雜的大規模軟體系統劃分成幾個規模較小、相對簡單的模組,即分而治之。面向對象程式設計方法是按對象來劃分。面向對象的軟體系統由對象組成,對象之間通過消息傳遞互相聯繫,如圖3.4所示。面向對象的軟體系統的組成
對象1對象2對象3對象4圖中箭頭表示對象之間的消息傳遞對象作為程式模組面向對象的軟體系統中每一個模組都是高度獨立的對象,而對象是比功能模組粒度更大的模組。對象是由數據和對數據的操作(代碼)形成的一個整體。面向對象程式設計採用封裝的辦法,使對象的內部實現與外界隔離,實現了資訊隱藏,從而提供了更理想的模組化機制,顯著地減少了程式模組間的相互干擾和依賴性。
數據抽象技術
將數據和對數據的操作封裝在一起,作為一個相互依存、不可分割的整體——對象來處理,它採用數據抽象和資訊隱藏技術。它將具有相同特徵的對象抽象成一個新的數據類型——類,並且考慮不同對象之間的聯繫和對象類的重用性3.4數據抽象3.4.1什麼是抽象?3.4.2數據抽象和抽象數據類型抽象性的例子classstudent{ intnumber; char*name; floatscore;public: student(intnumber1,char*name1,floatscore1); ~student(); voidmodify(floatscore1); voidprint();};抽象是有選擇地忽略。抽象性(Abstraction)抽象是對複雜的現實世界的簡明的表示。抽象強調了我們所關心的(感興趣)的資訊,而將我們不關心的其他資訊忽略。名家之言E.Dijkstra:“設計並實現一個大規模的軟體的中心問題是怎樣減小複雜度,一個途徑就是通過抽象”。BjarneStroustrup說:“在C++語言中,我一直在努力提高程式設計的抽象層次”。抽象數據類型一個值集和作用在值集上的操作集
C++語言中的一個類就是一個抽象數據類型每個抽象數據類型自成一個模組,模組的介面和內部實現分離開來,使用模組的應用程式員只需要知道如何使用該模組,而不必知道模組內部是如何實現的,也就是說,可以忽略模組內部的實現細節。
3.5封裝性和資訊隱藏所謂封裝(encapsulation),就是包含和隱藏對象資訊,如內部數據結構和代碼的能力。在面向對象的程式中,通過創建對象將代碼和數據捆綁在一起,並且在對象中包含了所有必需的代碼和數據。對象是支持封裝的元素。一個程式中的對象把現實世界中的實體的屬性和行為封裝成一個整體。資訊隱藏(informationhiding)
封裝的好處封裝性降低了程式設計的複雜度。
封裝將操作對象的內部複雜性與應用程式的其他部分隔離開來。應用程式員只需要通過對象的介面來操作對象完成特定的任務,而不需要知道對象內部複雜的細節。
封裝的好處(續)提高了代碼的安全性和可靠性;通過類實現封裝,集中和統一了對類中數據成員的所有操作,從而可避免因分散操作造成的錯誤。能有效提到程式模組的獨立性、可重用性和可維護性;只要類的介面(即類的共有部分)保持不變,類的結構部分的任何變化(包括數據結構和演算法)都不會對使用該類的根源程式有所影響,根源程式可以不做任何修改。面向對象程式設計的主要特徵抽象性封裝性繼承性多態性3.6繼承性與軟體重用面向對象程式設計的第二個主要特徵就是繼承性,繼承的目的就是為了重用(reuse)。
繼承,就是在一個已有的類的基礎上創建一個新類
,這個新類獲得了已有類的數據和操作代碼,並且新類可以增加新的數據和操作代碼。
繼承與派生問題舉例派生類的概念在已有類的基礎上新增自己的特性而產生新類的過程稱為派生。被繼承的已有類稱為基類(或父類)。派生出的新類稱為派生類(或子類)。參見圖3.5類的繼承
繼承與派生的目的繼承的目的:實現代碼重用。派生的目的:當新的問題出現,原有程式無法解決(或不能完全解決)時,需要對原有程式進行改造。C++中類的繼承層次自然地表達了人們分析問題時所用的分類結構。大大改善了軟體系統的可理解性和可維護性。
繼承的好處(1)繼承性很好地實現了代碼的重用。(2)能改進軟體系統的可維護性。(3)繼承性使已有的程式庫具有清晰的層次結構關係。3.7多態性廣義多態性:自然語言中的多態性;面向對象程式設計中的多態性什麼是多態性?(廣義)polymorphism,“manyforms”:即多種形態在自然語言中,多態性即是“一詞多義”;更準確地說,多態性是指相同的動詞作用到不同類型的對象上,例如:
駕駛摩托車 駕駛汽車 駕駛飛機 駕駛輪船駕駛太空船什麼是多態性?(OOP)當不同對象接受到相同的消息產生不同的動作,這種性質稱為多態性。通俗地說,多態性是指用一個名字定義不同的函數,這些函數執行不同但又類似的操作,即用同樣的介面訪問功能不同的函數,從而實現“一個介面,多種方法”。多態性的例子在C語言中,由於不支持多態,求絕對值的動作要求三個不同的函數名字:
abs(),labs(),fabs()分別用來求整數,長整數、浮點數的絕對值。在C++語言中,由於支持多態,求絕對值的動作可以只用一個函數名:abs()應用多態性的好處多態應用於OOP的目的是允許用一個名字來指定動作的一般類(即邏輯上相似的動作)。從而帶來以下的好處:提高了處理問題的抽象級別;降低了程式設計時的複雜性;增強了軟體的靈活性。面向對象程式設計的優越性重用性:有利於軟體生產率的提高;符合人類的自然思維習慣,能夠自然地描述現實世界的實體和問題:有利於控制軟體的複雜性;更好的模組化,便於多人分工和合作開發軟體;編寫的代碼有更好的可維護性;3.8面向對象的程式設計語言3.8.1對象程式設計語言的發展概況3.8.2幾種典型的面向對象程式設計語言幾種典型的OOPLSmalltalkC++JavaC#
4.1對象數組(Objectarrays)定義: 類名數組名[元素個數];例如:StudentaSA[10];//一個學生類對象數組通過下標訪問方法: 數組名[下標].成員名;例如:aSA[j].print();對象數組初始化數組中每一個元素對象被創建時,系統都會調用構造函數類初始化該對象。通過初始化列表賦值。例:
LocationA[2]={Location(1,2),Location(3,4)};如果沒有為數組元素指定顯式初始值,數組元素使用缺省值初始化(調用缺省構造函數)。當數組中每一個對象被刪除時,系統都要調用一次析構函數。數組元素所屬類的構造函數沒有自定義構造函數時,則採用缺省構造函數。各元素對象的初值要求為相同的值時,可以定義出具有缺省形參值的構造函數。各元素對象的初值要求為不同的值時,需要定義帶形參(無缺省值)的構造函數。如果需要定義動態數組,需要一個無參構造函數。對象數組例子#include<iostream.h>classsamp{inta,b;public:samp(intn,intm){a=n;b=m;}intget_a(){returna;}intget_b(){returnb;}};對象數組例子(續)main(){sampob[4][2]={samp(1,2),samp(3,4),samp(5,6),samp(7,8),samp(9,10),samp(11,12),samp(13,14),samp(15,16),};inti;對象數組例子(續)
for(i=0;i<4;i++){cout<<ob[i][0].get_a();cout<<ob[i][0].get_b()<<"\n";cout<<ob[i][1].get_a();cout<<ob[i][1].get_b()<<"\n";}cout<<"\n";return0;}4.2指向對象的指針可以定義指向對象的指針,在運用對象的指針的時候,對象的成員將用箭頭運算符(→)而不是點運算符(.)引用。對象的指針演算法與其它數據類型的指針演算法相同:它被與對象相聯系的處理。例如:當對象指針增加的時候,它指向下一個對象;當對象指針減少的時候,它指向前一個對象。對象指針的用法
intmain(){
Circlec1(3),*pc;//pc為指向圓形Circle類對象的指針
pc=&c1; //將對象c1的地址賦給對象指針pc,使其指向圓形對象c1
cout<<c1.GetArea()<<endl; //通過對象名訪問對象的方法
cout<<pc->GetArea()<<endl;//通過對象指針訪問對象的方法
return0;}對象指針與對象數組#include"student.h"intmain(){… Student*sp=aSA; //給指針賦初值
for(inti=0;i<4;i++,sp++)sp->print();//指針加1後,指向下一個對象
sp=sp-3; //指針減3後,指針向前移動三個對象
sp->modify(87); sp->print(); return0;}4.3this指針對象是由數據和操作構成的一個整體,即使同一類的不同對象的操作也是相互獨立的,各占不同的記憶體空間。但是,在C++實現中,這種處理方案太浪費記憶體空間。同類的對象能否共用該類成員函數的同一個實例呢?myBirthday.set(31,12,1997);nationalDay.set(1.10,1949);voidDate::Set(intD,intM,inty){day=D;month=M;year=Y;}那麼,此時電腦怎麼能區分該函數是作用在哪個對象上呢?解決方案C++在編譯時,自動在每個成員函數中的第一個參數位置增加:
X*constthis;
當對象調用該函數時,編譯器自動將該對象的指針作為實參傳遞給相應的函數,這樣就可解決上述問題:voidDate::Set(Date*constthis,intD,intM,intY){this->day=D;this->month=M;this->year=Y;}
注:程式員不能將this指針的說明寫在函數中C的結構體struct與C++的struct的比較C語言的結構體中數據與操作是分離的
在C++語言中將數據與操作封裝在一個結構體中
structStudent{ intnumber; charname[15]; floatscore;};數據成員structStudent{intnumber;charname[15];floatscore;voiddisplay(Student*this)//函數成員{ cout<<”number:”<<this->number; cout<<”name:”<<this->name; cout<<”score:”<<this->score<<endl;}};/*獨立函數display*/voiddisplay(Student*stu){ printf(”number:%d”,stu->number); printf(”name:%s”,stu->name); printf(”score:%f\n”,stu->score);}數據成員this指針this指針是由C++編譯器自動產生而使用的一個隱含指針,類成員函數使用該指針來存取對象的數據成員。
定義同一類的多個對象時,每個對象擁有自己的數據成員但它們共用一份成員函數,當成員函數存取對象的數據時它們使用this指針,通過this指針指向不同對象來決定使用哪一個對象的數據成員。this指針是如何在“幕後”工作?例如complex類中的init函數:voidinit(doubler,doublei){real=r;image=i;}C++編譯器在編譯這個函數時自動插入this指針:voidinit(complex*this,doubler,doublei){this->real=r;this->image=i;}this指針的顯式使用this指針是系統的一個內部指針,通常以隱式方式使用,但也可以為程式員所使用,例如,當運算符重載和建立鏈表時this指針顯得十分重要。參見雙向鏈表This指針的顯式用法顯式地使用this指針,還可以使C++的編譯器將同名的參數與數據成員區分開來:voidDate::Set(intday,intmonth,intyear){this->day=day;this->month=month;this->year=year;}4.4對象的賦值如果兩個對象屬於同一種類型,那麼我們可以將其中的一個對象的值(屬性值)賦給另一個對象。在默認情況下,當將一個對象賦值給另一個對象時,第一個對象的數據將被按位複製到第二個對象中。對象賦值的例子【例4.4】
intmain(){Rectanglerect1(10,20),rect2(0,0);
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 供水合同范本模板模板
- 农业温室气体调控与净化设备创新创业项目商业计划书
- 虚拟学习助手创新创业项目商业计划书
- 军训服装租赁合同范本
- 体育用品采购协议合同
- 公司维修改造合同范本
- 人工话务外包合同范本
- 农田种植服务合同范本
- 小学语文教师面试教学设计指引
- 企业员工劳动合同签订指南模板
- 专题15 完形填空记叙文(20空)-2020-2024年五年高考英语真题分类汇编(全国版)(解析版)
- 2024年度造纸厂并购合同
- 《大学课件钻井液》课件
- 资深船长:18天终于把船舶抵达美国加州的注意事项理清了
- 期中试卷(试题)2024-2025学年数学六年级上册北师大版
- 植物生理学第七章细胞信号转导
- DB32T-乡镇农产品质量安全监管机构建设规范编制说明
- 《全面质量管理(习题集)》学习考试题库资料(含答案)
- 新收入准则在电商企业中的应用探析-以阿里巴巴为例
- CJ/T 124-2016 给水用钢骨架聚乙烯塑料复合管件
- 一例晚期直肠癌多发转移患者镇痛治疗病例分享
评论
0/150
提交评论