版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第12章類的其它特性友元函數類中私有和保護的成員在類外不能被訪問。友元函數是一種定義在類外部的普通函數,其特點是能夠訪問類中私有成員和保護成員,即類的訪問權限的限制對其不起作用。友元函數需要在類體內進行說明,在前面加上關鍵字friend。一般格式為:friend<type>FuncName(<args>);friendfloatVolume(A&a);關鍵字返回值類型函數名函數參數友元函數不是成員函數,用法也與普通的函數完全一致,只不過它能訪問類中所有的數據。友元函數破壞了類的封裝性和隱蔽性,使得非成員函數可以訪問類的私有成員。一個類的友元可以自由地用該類中的所有成員。classA{ floatx,y;public:A(floata,floatb){x=a;y=b;}floatSum(){returnx+y;}
friendfloatSum(A&a){returna.x+a.y; }};voidmain(void){At1(4,5),t2(10,20);
cout<<t1.Sum()<<endl;
cout<<Sum(t2)<<endl;}友元函數成員函數友元函數的調用,直接調用成員函數的調用,利用對象名調用友元函數只能用對象名引用類中的數據。私有數據有關友元函數的使用,說明如下:友元函數不是類的成員函數友元函數近似于普通的函數,它不帶有this指針,因此必須將對象名或對象的引用作為友元函數的參數,這樣才能訪問到對象的成員。友元函數與一般函數的不同點在于:友元函數必須在類的定義中說明,其函數體可在類內定義,也可在類外定義;它可以訪問該類中的所有成員(公有的、私有的和保護的),而一般函數只能訪問類中的公有成員。classA{ floatx,y;public:A(floata,floatb){x=a;y=b;}floatGetx(){returnx;}floatGety(){returny;}floatSum(){returnx+y;}
friendfloatSum(A&);};floatSumxy(A&a){returna.Getx()+a.Gety();}floatSum(A&a){returna.x+a.y;}voidmain(void){At1(1,2),t2(10,20),t3(100,200);
cout<<t1.Sum()<<endl;
cout<<Sum(t2)<<endl;
cout<<Sumxy(t3)<<endl;}成員函數友元函數,可以直接調用類中私有成員普通函數,必須通過公有函數訪問私有成員對象調用成員函數調用友元函數調用一般函數友元函數友元函數不受類中訪問權限關鍵字的限制,可以把它放在類的私有部分,放在類的公有部分或放在類的保護部分,其作用都是一樣的。換言之,在類中對友元函數指定訪問權限是不起作用的。友元函數的作用域與一般函數的作用域相同。謹慎使用友元函數通常使用友元函數來取對象中的數據成員值,而不修改對象中的成員值,則肯定是安全的。大多數情況是友元函數是某個類的成員函數,即A類中的某個成員函數是B類中的友元函數,這個成員函數可以直接訪問B類中的私有數據。這就實現(xiàn)了類與類之間的溝通。注意:一個類的成員函數作為另一個類的友元函數時,應先定義友元函數所在的類。classA{...voidfun(B&);};classB{...friendvoidfun(B&);};既是類A的成員函數又是類B的友元函數classB; //先定義類A,則首先對類B作引用性說明classA{...... //類A的成員定義
public:voidfun(B&);//函數的原型說明
};classB{ ......friendvoidA::fun(B&);//定義友元函數};
voidA::fun(B&b) //函數的完整定義{...... //函數體的定義}類A中的成員函數fun()是類B的友元函數。即在fun()中可以直接引用類B的私有成員。classB; //必須在此進行引用性說明,classA{ floatx,y;public:A(floata,floatb){x=a;y=b;}
floatSum(B&);//說明友元函數的函數原型,是類A的一成員函數};classB{ floatm,n;public:B(floata,floatb){m=a;n=b;}
friendfloatA::Sum(B&);//說明類A的成員函數是類B的友元函數}floatA::Sum(B&b) //定義該友元函數{x=b.m+b.n;y=b.m-b.n;}voidmain(void){Aa1(3,5);Bb1(10,20);
a1.Sum(b1); //調用該函數,因是類A的成員函數,故用類A的對象調用}a1.x=30a1.y=-10直接引用類B的私有成員類A中有一個函數可以直接引用類B的私有成員友元類classA{.....friendclassB;}classB{.....}類B是類A的友元類B可以自由使用類A中的成員對于類B而言,類A是透明的類B必須通過類A的對象使用類A的成員constfloatPI=3.1415926;classA{ floatr; floath;public: A(floata,floatb){r=a;h=b;} floatGetr(){returnr;} floatGeth(){returnh;}
friendclassB;//定義類B為類A的友元};classB{intnumber;public: B(intn=1) {number=n;} voidShow(A&a) {cout<<PI*a.r*a.r*a.h*number<<endl;}//求類A的某個對象*n的體積};voidmain(void){ Aa1(25,40),a2(10,40); Bb1(2); b1.Show(a1); b1.Show(a2);}直接引用類A的私有成員類B中的任何函數都能使用類A中的所有私有成員。不管是按哪一種方式派生,基類的私有成員在派生類中都是不可見的。如果在一個派生類中要訪問基類中的私有成員,可以將這個派生類聲明為基類的友元。classBase{friendclassDerive;.....}classDerive{.....}直接使用Base中的私有成員#include<iostream.h>classM{friendclassN;//N為M的友元,可以直接使用M中的私有成員private:inti,j;voidshow(void){cout<<"i="<<i<<'\t'<<"j="<<j<<'\t';}public:M(inta=0,intb=0){i=a;j=b;}};classN:publicM{//N為M的派生類public:N(inta=0,intb=0):M(a,b){}voidPrint(void){show();cout<<"i+j="<<i+j<<endl; }};voidmain(void){Nn1(10,20);Mm1(100,200);//m1.show(); //私有成員函數,在類外不可調用
n1.Print();}直接引用類M的私有成員函數和私有成員基類對象M派生類對象Nx(私有)Show()(私有)x(私私有)Show()(私私有)y(公有)Showy()(公有)showy(){show();cout<<i<<endl;}當派生類是基類的友元時,上式可以調用Nn;n.showy();n.show();錯誤,類外不可調用虛函數多態(tài)性:調用同一個函數名,可以根據需要但實現(xiàn)不同的功能。多態(tài)性是面向對象的程序設計的關鍵技術。編譯時的多態(tài)性(函數重載)運行時的多態(tài)性(虛函數)多態(tài)性運行時的多態(tài)性是指在程序執(zhí)行之前,根據函數名和參數無法確定應該調用哪一個函數,必須在程序的執(zhí)行過程中,根據具體的執(zhí)行情況來動態(tài)地確定可以將一個派生類對象的地址賦給基類的指針變量?;悓ο笈缮悓ο驜aseb;Derived;Base*basep;basepbasep=&b;basepbasep=&d;basep只能引用從基類繼承來的成員。xShow()xShow()yShow()basep->Show();basep->Show()基類指針派生類對象基類對象classPoint{ floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
floatarea(void) { return0.0; }};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類
floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius; }};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針
Circlec(5.4321);pp=&c;cout<<pp->area()<<endl; //調用的是基類中有的公有函數}在基類和派生類中具有相同的公有函數area()。在這種情況下,使用基類的指針時,只能訪問從相應基類中繼承來的成員,而不允許訪問在派生類中增加的成員。輸出為0基類對象派生類對象Baseb;Derived;basepbasepxShow()xShow()yShow()basep->Show()Base*basep;basep=&b;basep=&d;basep->Show();即指向派生類新增的成員函數需要將基類中的Show()說明為虛函數若要訪問派生類中相同名字的函數,必須將基類中的同名函數定義為虛函數,這樣,將不同的派生類對象的地址賦給基類的指針變量后,就可以動態(tài)地根據這種賦值語句調用不同類中的函數。classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
virtual
floatarea(void) {return0.0;}};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類
floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius;}};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針
Circlec(5.4321);pp=&c;cout<<pp->area()<<endl;//調用虛函數}將area()聲明為虛函數,編譯器對其進行動態(tài)聚束,按照實際對象c調用了Circle中的函數area()。使Point類中的area()與Circle類中的area()有一個統(tǒng)一的接口。輸出:92.7011聲明為虛函數調用虛函數虛函數再定義虛函數的定義和使用
可以在程序運行時通過調用相同的函數名而實現(xiàn)不同功能的函數稱為虛函數。定義格式為:virtual<type>FuncName(<ArgList>);一旦把基類的成員函數定義為虛函數,由基類所派生出來的所有派生類中,該函數均保持虛函數的特性。在派生類中重新定義基類中的虛函數時,可以不用關鍵字virtual來修飾這個成員函數。虛函數是用關鍵字virtual修飾的某基類中的protected或public成員函數。它可以在派生類中重新定義,以形成不同版本。只有在程序的執(zhí)行過程中,依據指針具體指向哪個類對象,或依據引用哪個類對象,才能確定激活哪一個版本,實現(xiàn)動態(tài)聚束。classA{protected: intx;public: A(){x=1000;}
virtualvoidprint(){ cout<<“x=”<<x<<‘\t’; }//虛函數};classB:publicA{ inty;public: B(){y=2000;}
voidprint(){ cout<<“y=”<<y<<‘\t’; }//派生虛函數}; classC:publicA{ intz;public: C(){z=3000;}
voidprint(){ cout<<“z=”<<z<<‘\n’; }//派生虛函數};voidmain(void){Aa,*pa;Bb; Cc;a.print();b.print(); c.print();//靜態(tài)調用
pa=&a;pa->print();//調用類A的虛函數
pa=&b;pa->print();//調用類B的虛函數
pa=&c;pa->print();}//調用類C的虛函數classBase{public:virtualintSet(inta,intb){.....}....};classDerive:publicBase{public:intSet(intx,inty){.....}.....};classBase{public:virtualintSet(inta,intb){.....}....};classDerive:publicBase{public:intSet(intx,inty=0){.....}.....};intSet(int,int)是虛函數兩個Set()函數參數不一致,是重載,不是虛函數關于虛函數,說明以下幾點:1、當在基類中把成員函數定義為虛函數后,在其派生類中定義的虛函數必須與基類中的虛函數同名,參數的類型、順序、參數的個數必須一一對應,函數的返回的類型也相同。若函數名相同,但參數的個數不同或者參數的類型不同時,則屬于函數的重載,而不是虛函數。若函數名不同,顯然這是不同的成員函數。2、實現(xiàn)這種動態(tài)的多態(tài)性時,必須使用基類類型的指針變量,并使該指針指向不同的派生類對象,并通過調用指針所指向的虛函數才能實現(xiàn)動態(tài)的多態(tài)性。xShow()xShow()yShow()xShow()zShow()類A類B類CShow()定義為虛函數類B與類C均為類A的公有派生。A*p;Bb;Cc;p=&b;p->Show();p=&c;p->Show();即在程序運行時,通過賦值語句實現(xiàn)多態(tài)性3、虛函數必須是類的一個成員函數,不能是友元函數,也不能是靜態(tài)的成員函數。4、在派生類中沒有重新定義虛函數時,與一般的成員函數一樣,當調用這種派生類對象的虛函數時,則調用其基類中的虛函數。5、可把析構函數定義為虛函數,但是,不能將構造函數定義為虛函數。6、虛函數與一般的成員函數相比較,調用時的執(zhí)行速度要慢一些。為了實現(xiàn)多態(tài)性,在每一個派生類中均要保存相應虛函數的入口地址表,函數的調用機制也是間接實現(xiàn)的。因此,除了要編寫一些通用的程序,并一定要使用虛函數才能完成其功能要求外,通常不必使用虛函數。7、一個函數如果被定義成虛函數,則不管經歷多少次派生,仍將保持其虛特性,以實現(xiàn)“一個接口,多個形態(tài)”。虛函數的訪問用基指針訪問與用對象名訪問用基指針訪問虛函數時,指向其實際派生類對象重新定義的函數。實現(xiàn)動態(tài)聚束。通過一個對象名訪問時,只能靜態(tài)聚束。即由編譯器在編譯的時候決定調用哪個函數。classPoint{floatx,y;public: Point(){} Point(floati,floatj){ x=i; y=j; }
virtual
floatarea(void) {return0.0;}//聲明為虛函數};constfloatPi=3.14159;classCircle:publicPoint{ //類Point的派生類
floatradius;public: Circle(floatr){ radius=r; }
floatarea(void) {returnPi*radius*radius;}//虛函數再定義};voidmain(void){Point*pp; //基類指針,可以將派生類對象的地址賦給基類指針
Circlec(5.4321);cout<<c.area()<<endl;cout<<c.Point::area()<<endl; cout<<c.Circle::area()<<endl;}輸出:92.7011 0 92.7011可見,利用對象名進行調用與一般非虛函數沒有區(qū)別。用對象名調用area()classbase0{public: voidv(void){ cout<<"base0\n"; }};classbase1:publicbase0{public: virtualvoidv(void){cout<<"base1\n";}};classA1:publicbase1{public: voidv(){ cout<<"A1\n"; }};classA2:publicA1{public: voidv(void){ cout<<"A2\n"; }};classB1:privatebase1{public: voidv(void){ cout<<"B1\n"; }};classB2:publicB1{public: voidv(void){ cout<<"B2\n"; }};voidmain(void){base0*pb;A1a1;(pb=&a1)->v();A2a2;(pb=&a2)->v();B1b1;
(pb=&b1)->v();B2b2;
(pb=&b2)->v();}base0base0私有派生,在類外不能調用基類函數classbase0{public: voidv(void){ cout<<"base0\n"; }};classbase1:publicbase0{public: virtualvoidv(void){cout<<"base1\n";}};classA1:publicbase1{public: voidv(){ cout<<"A1\n"; }};classA2:publicA1{public: voidv(void){ cout<<"A2\n"; }};classB1:privatebase1{public: voidv(void){ cout<<"B1\n"; }};classB2:publicB1{public: voidv(void){ cout<<"B2\n"; }};voidmain(void){base1*pb;A1a1;(pb=&a1)->v();A2a2;(pb=&a2)->v();}A1A2純虛函數在基類中不對虛函數給出有意義的實現(xiàn),它只是在派生類中有具體的意義。這時基類中的虛函數只是一個入口,具體的目的地由不同的派生類中的對象決定。這個虛函數稱為純虛函數。class<基類名>{ virtual<類型><函數名>(<參數表>)=0; ......};classA{protected: intx;public: A(){x=1000;}
virtualvoidprint()=0;//定義純虛函數};classB:publicA{//派生類private:inty;public: B(){y=2000;}
voidprint(){cout<<“y=”<<y<<‘\n’;}//重新定義純虛函數};
classC:publicA{//派生類
intz;public: C(){z=3000;}
voidprint(){cout<<“z=”<<z<<‘\n’;}//重新定義純虛函數};voidmain(void){A*pa; Bb; Cc;pa=&b;pa->print(); pa=&c;pa->print();
Aa;pa=&a;pa->print();}y=2000z=3000抽象類不能定義抽象類的對象1、在定義純虛函數時,不能定義虛函數的實現(xiàn)部分。2、把函數名賦于0,本質上是將指向函數體的指針值賦為初值0。與定義空函數不一樣,空函數的函數體為空,即調用該函數時,不執(zhí)行任何動作。在沒有重新定義這種純虛函數之前,是不能調用這種函數的。3、把至少包含一個純虛函數的類,稱為抽象類。這種類只能作為派生類的基類,不能用來說明這種類的對象。其理由是明顯的:因為虛函數沒有實現(xiàn)部分,所以不能產生對象。但可以定義指向抽象類的指針,即指向這種基類的指針。當用這種基類指針指向其派生類的對象時,必須在派生類中重載純虛函數,否則會產生程序的運行錯誤。4、在以抽象類作為基類的派生類中必須有純虛函數的實現(xiàn)部分,即必須有重載純虛函數的函數體。否則,這樣的派生類也是不能產生對象的。綜上所述,可把純虛函數歸結為:抽象類的唯一用途是為派生類提供基類,純虛函數的作用是作為派生類中的成員函數的基礎,并實現(xiàn)動態(tài)多態(tài)性。虛基類多基派生中的多條路徑具有公共基類時,在這條路徑的匯合處就會因對公共基類產生多個拷貝而產生同名函數調用的二義性。解決這個問題的辦法就是把公共基類定義為虛基類,使由它派生的多條路徑的匯聚處只產生一個拷貝。classBase{};classA:publicBase{};classB:publicBase{};classC:publicA,publicB{};類C中繼承了兩個類Base,即有兩個類Base的實現(xiàn)部分,在調用時產生了二義性。用虛基類進行多重派生時,若虛基類沒有缺省的構造函數,則在每一個派生類的構造函數中都必須有對虛基類構造函數的調用(且首先調用)。由虛基類派生出的對象初始化時,直接調用虛基類的構造函數。因此,若將一個類定義為虛基類,則一定有正確的構造函數可供所有派生類調用。classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:publicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:publicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidb(){ cout<<"b()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;
base*pbase=&cc;//錯誤
A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類C的地址賦值時產生歧義a()b()c()d()e()f()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()baseABCa()d()ABclassbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:publicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:publicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidb(){ cout<<"b()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;
base*pbase=&cc;//錯誤
A*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}將類C的地址賦值時產生歧義類C中有兩個base,只有一個Aa()inCb()inAc()inbased()inCe()inbasef()inA為避免這種情況,將base定義為虛基類。classbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:virtualpublicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:virtualpublicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;base*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}a()b()c()d()e()f()a()b()c()d()e()f()a()b()f()a()b()c()d()e()f()a()c()a()b()c()d()e()f()a()b()f()a()c()baseABCa()d()ABclassbase{public:virtualvoida(){ cout<<"a()inbase\n";}virtualvoidb(){ cout<<"b()inbase\n";}virtualvoidc(){ cout<<"c()inbase\n";}virtualvoidd(){ cout<<"d()inbase\n";}virtualvoide(){ cout<<"e()inbase\n";}virtualvoidf(){ cout<<"f()inbase\n";}};classA:virtualpublicbase{public: virtualvoida(){ cout<<"a()inA\n";}virtualvoidb(){ cout<<"b()inA\n";}virtualvoidf(){ cout<<"f()inA\n";}};classB:virtualpublicbase{public:virtualvoida(){ cout<<"a()inB\n";}virtualvoidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: virtualvoida(){ cout<<"a()inC\n";}virtualvoidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;base*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}類C中只有一個basea()inCb()inAc()inBd()inCe()inbasef()inAclassbase{public:voida(){cout<<"a()inbase\n";}voidb(){cout<<"b()inbase\n";}voidc(){ cout<<"c()inbase\n";}voidd(){cout<<"d()inbase\n";}voide(){ cout<<"e()inbase\n";}voidf(){ cout<<"f()inbase\n";}};classA:virtualpublicbase{public: voida(){cout<<"a()inA\n";}voidb(){cout<<"b()inA\n";}voidf(){ cout<<"f()inA\n";}};classB:virtualpublicbase{public:voida(){cout<<"a()inB\n";}voidc(){ cout<<"c()inB\n";}};classC:publicA,publicB{public: voida(){ cout<<"a()inC\n";}voidd(){ cout<<"d()inC\n";}};voidmain(void){Ccc;base*pa=&cc;pa->a();pa->b();pa->c();pa->d();pa->e();pa->f();}類C中只有一個basea()inbaseb()inbasec()inbased()inbasee()inbasef()inbase下面程序的輸出是
。classA{protected:intx;public:A(){x=1000;}virtualvoidp(){cout<<"x="<<x<<'\n';p2();}virtualvoidp2(){cout<<"A::p2()"<<endl;}};classC:publicA{ intz;public:C(){z=3000;}voidp(){cout<<"z="<<z<<'\n';p2();}virtualvoidp2(){cout<<"C::p2()"<<endl;}};voidmain(void){Cc;Aa,*pa=&a;pa->p();pa=&c;pa->p();}
通常,每當說明一個對象時,把該類中的有關成員數據拷貝到該對象中,即同一類的不同對象,其成員數據之間是互相獨立的。靜態(tài)成員classA{intx,y;public:voidSetxy(inta,intb){x=a;y=b;}};Aa1,a2;a1.xa1.ya2.xa2.y......x=a;y=b;......a1.Setxy()a2.Setxy()a1.Setxy(1,2);a2.Setxy(3,4);this->x=a;this->y=b;當我們將類的某一個數據成員的存儲類型指定為靜態(tài)類型時,則由該類所產生的所有對象,其靜態(tài)成員均共享一個存儲空間,這個空間是在編譯的時候分配的。換言之,在說明對象時,并不為靜態(tài)類型的成員分配空間。在類定義中,用關鍵字static修飾的數據成員稱為靜態(tài)數據成員。classA{intx,y;staticintz;public:voidSetxy(inta,intb){x=a;y=b;}};Aa1,
a2;a1.xa1.ya2.xa2.yza1.za2.z不同對象,同一空間有關靜態(tài)數據成員的使用,說明以下幾點:1、類的靜態(tài)數據成員是靜態(tài)分配存儲空間的,而其它成員是動態(tài)分配存儲空間的(全局變量除外)。當類中沒有定義靜態(tài)數據成員時,在程序執(zhí)行期間遇到說明類的對象時,才為對象的所有成員依次分配存儲空間,這種存儲空間的分配是動態(tài)的;而當類中定義了靜態(tài)數據成員時,在編譯時,就要為類的靜態(tài)數據成員分配存儲空間。2、必須在文件作用域中,對靜態(tài)數據成員作一次且只能作一次定義性說明。因為靜態(tài)數據成員在定義性說明時已分配了存儲空間,所以通過靜態(tài)數據成員名前加上類名和作用域運算符,可直接引用靜態(tài)數據成員。在C++中,靜態(tài)變量缺省的初值為0,所以靜態(tài)數據成員總有唯一的初值。當然,在對靜態(tài)數據成員作定義性的說明時,也可以指定一個初值。classA{ inti,j;
staticintx,y;//定義靜態(tài)成員public: A(inta=0,intb=0,intc=0,intd=0){ i=a;j=b;x=c;y=d; } voidShow(){cout<<"i="<<i<<'\t'<<"j="<<j<<'\t'; cout<<"x="<<x<<'\t'<<"y="<<y<<"\n"; }};intA::x=0;//必須對靜態(tài)成員作一次定義性說明intA::y=0;
voidmain(void){ Aa(2,3,4,5); a.Show(); Ab(100,200,300,400); b.Show(); a.Show();}a.x和b.x在內存中占據一個空間a.y和b.y在內存中占據一個空間i=2 j=3 x=4 y=5i=100 j=200 x=300 y=400i=2 j=3 x=300 y=400classA{ inti,j;public:staticintx;public:A(inta=0,intb=0,intc=0){i=a;j=b;x=c; } voidShow(){ cout<<"i="<<i<<'\t'<<"j="<<j<<'\t'; cout<<"x="<<x<<"\n"; }};intA::x=500; //intA::xvoidmain(void){ Aa(20,40,10),b(30,50,100); a.Show(); b.Show(); cout<<“A::x=”<<A::x<<‘\n’;//可以直接用類名引用 }在類外重新定義3、靜態(tài)數據成員具有全局變量和局部變量的一些特性。靜態(tài)數據成員與全局變量一樣都是靜態(tài)分配存儲空間的,但全局變量在程序中的任何位置都可以訪問它,而靜態(tài)數據成員受到訪問權限的約束。必須是public權限時,才可能在類外進行訪問。4、為了保持靜態(tài)數據成員取值的一致性,通常在構造函數中不給靜態(tài)數據成員置初值,而是在對靜態(tài)數據成員的定義性說明時指定初值。classA{ inti;
staticintcount;public:
A(inta=0) {i=a;count++; cout<<"NumberofObjects="<<count<<'\n'; } ~A() {count--;cout<<"NumberofObjects="<<count<<'\n'; }
voidShow() {cout<<"i="<<i<<'\n';cout<<"count="<<count<<"\n";}};intA::count;voidmain(void){ Aa1(100); Ab[2]; a1.Show();}NumberofObjects=1NumberofObjects=2NumberofObjects=3i=100count=3NumberofObjects=2NumberofObjects=1NumberofObjects=0靜態(tài)成員函數可以將類的成員函數定義為靜態(tài)的成員函數。即使用關鍵字static來修飾成員函數。classA{floatx,y;public:A(){}staticvoidsum(void){.....}};對靜態(tài)成員函數的用法說明以下幾點:1、與靜態(tài)數據成員一樣,在類外的程序代碼中,通過類名加上作用域操作符,可直接調用靜態(tài)成員函數。2、靜態(tài)成員函數只能直接使用本類的靜態(tài)數據成員或靜態(tài)成員函數,但不能直接使用非靜態(tài)的數據成員(可以引用使用)。這是因為靜態(tài)成員函數可被其它程序代碼直接調用,所以,它不包含對象地址的this指針。classTc{private:intA;
staticintB;//靜態(tài)數據成員public:Tc(inta){A=a;B+=a;}
staticvoiddisplay(Tcc)//Tc的對象為形參
{ cout<<"A="<<c.A<<",B="<<B<<endl; }};intTc::B=2;voidmain(void){ Tca(2),b(4);
Tc::display(a);
Tc::display(b);}非靜態(tài)成員,用對象名來引用靜態(tài)成員,直接引用直接用類名來調用靜態(tài)成員函數A=2,B=8A=4,B=83、靜態(tài)成員函數的實現(xiàn)部分在類定義之外定義時,其前面不能加修飾詞static。這是由于關鍵字static不是數據類型的組成部分,因此,在類外定義靜態(tài)成員函數的實現(xiàn)部分時,不能使用這個關鍵字4、不能把靜態(tài)成員函數定義為虛函數。靜態(tài)成員函數也是在編譯時分配存儲空間,所以在程序的執(zhí)行過程中不能提供多態(tài)性。5、可將靜態(tài)成員函數定義為內聯(lián)的(inline),其定義方法與非靜態(tài)成員函數完全相同。classTc{private:intA;
staticintB;//靜態(tài)數據成員public:Tc(inta){A=a;B+=a;}
staticvoiddisplay(Tcc);//Tc的對象為形參};voidTc::display(Tcc)//不用static修飾
{cout<<"A="<<c.A<<",B="<<B<<endl; }intTc::B=2;voidmain(void){ Tca(2),b(4);
Tc::display(a);
Tc::display(b);}函數原型
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026中國特色農產品物流行業(yè)市場現(xiàn)狀分析與發(fā)展規(guī)劃評估研究
- 2026中國醫(yī)療器械研發(fā)行業(yè)市場分析技術趨勢及投資評估報告
- 2026潛水艇觀光產業(yè)發(fā)展規(guī)劃與海洋資源開發(fā)管理機制完善
- 2026中國網絡游戲行業(yè)市場競爭態(tài)勢與投資前景分析研究報告
- 2026中國現(xiàn)代農業(yè)行業(yè)市場需求變化分析及行業(yè)發(fā)展趨勢預測研究書
- 2026量子計算硬件性能突破與行業(yè)應用場景探索報告
- 2026氫氧化鋅光伏行業(yè)市場現(xiàn)狀供需分析及投資提升的評估規(guī)劃分析研究報告
- 2026中國云計算服務出海戰(zhàn)略與本地化合規(guī)挑戰(zhàn)報告
- 2026中國協(xié)作機器人市場規(guī)模測算與應用場景深化研究
- 2026中國漁業(yè)加工行業(yè)市場分析及發(fā)展戰(zhàn)略與投資前景預測研究報告
- 安全閥拆裝培訓課件
- 河南省養(yǎng)老類建筑消防設計技術要點2025
- 海天考研協(xié)議書
- 《電鍍工藝原理與應用》課件
- 職業(yè)學校班主任培訓材料
- 浙江省SBS改性瀝青混合料應用技術規(guī)程
- DL/T5315-2014水工混凝土建筑物修補加固技術規(guī)程(完整)
- 項目財務策劃
- 美容化妝培訓課件生活妝
- 《責任心與執(zhí)行力》課件
- 混凝土結構及砌體結構課件
評論
0/150
提交評論