友元函數:
- 說明: 在類型聲明中將原型放入並在原型聲明前加上關鍵字friend,但不要在定義時加上關鍵字friend,它的訪問權限與類的成員函數的訪問權限相同<聲明而已為private,public,protect>
- 作用: 把其他類中函數作為自己的友員成員函數,讓特定的類成員成為另一個類的友元函數,而不必讓整個類成為友元,但在使用時,必須小心排列各種聲明和定義順序(且需用到向前聲明)
- 向前聲明格式:
- class 類名 ;//向前聲明本類
- class 所用到的友員成員函數的類的類名 {....};//友元函數類的定義
- class 類名 {…}//本類的定義
- 注意: 友元函數不要在類的內部定義,不然編譯不會通過
- 例子:
友元函數(內部定義):
View Code class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a){ cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl;} private: double x, y; };
元函數(內外部定義):
View Code class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a); private: double x, y; }; void Getxy(Point &a) { cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl; }
友元成員函數:
View Code class Date; //對 Date 類的向前聲明 class Time //定義 Time 類 { public: Time(int,int,int); void display(Date&); //display 是 Time 類的成員函數,形參是 Date 類對象的引用 private: int hour; int minute; int sec; }; class Date { public: Date(int,int,int); friend void Time::display(Date&); //聲明 Time 類的 display函數為本類的友元函數 private: int month; int day; int year; }; Time::Time(int h,int m,int s) //定義 Time 類的構造函數 { hour = h; minute = m; sec = s; } void Time::display(Date&d) //display 函數的作用是輸出年、月、日和時、分、秒 { cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl; //引用 Date 類對象中的私有數據 cout<<hour<<":"<<minute<<":"<<sec<<endl; //引用本類對象中的私有數據 } Date::Date(int m,int d,int y) //定義 Date 類的構造函數 { month = m; day = d; year = y; } /* 請注意本程序的主函數中調用友元函數訪問有關類的私有數據的方法: 1、在函數名 display 的前面要加 display 所在的對象名(t1); 2、display 成員函數的實參是 Date 類對象 d1,否則就不能訪問 d1 中的私有數據; 3、在 Time::display 函數中引用 Date 類私有數據時必須加上對象名,如 d.month 。 */
虛函數:
- 作用: 如果想通過指向派生類對象的基類指針,調用派生類中覆蓋的成員函數,可以用虛函數的方法讓派生類中覆蓋的成員函數被調用
- 格式: virtual 返回類型 函數名 (形參表);
- 注意事項:
- 如果定義的類作為基類,則應將那些要在派生類中重新定義的類的方式聲明為虛擬的
- 如果使用指向對象的引用或指針調用虛方法,程序將使用做為對象類型的定義方法而不是使用做為了引用或指針定義的方法
- 工作原理:
- 給每個對象添加一個隱藏成員,隱藏成員中保存了個指 向函數的地址數組的指針,這種數組稱為虛函數表(vtbl)虛函數表中存儲了為類對象進行聲明的虛函數地址,派生類對象將包含一個指向獨立地址的表指針,如果派生類提供了虛函數的新的定義,該虛函數地址被添加到vtbl中,注意,無論類中包含的虛函數是1還是10個都只需在對象中添加一個地址成員,只是表大小不同而已
- 純虛函數:
- 格式: virtual 函數類型 函數名(形參表)=0;<無函數體>
- 作用: 有時,要在基類中不能給出虛函數實現代碼,這是可將這種虛函數聲明為純虛函數
- 注意: 有純虛函數的類都是抽象類,並且抽象類是不能產生實例的.
- 虛函數使用總結
- 如果你期望派生類從新定義一個成員函數,那么你應該在基類中把此函數設置為 virtual
- 以單一指令調用不同函數,這種性質稱為 Polymorphism(多態)
- 虛擬函數是 C++ 語言的 Polymorphism 性質以動態綁定的關鍵
- 既然抽象類中的虛函數不打算被調用,我們就不應該定義它,應該把它設置為純虛函數(在函數聲明后加 "=0" 即可)
- 我們可以說,擁有純虛函數者為抽象類(abstract class),以別於所謂的具體類(concrete class)
- 抽象類不能產生實例,但我們可以擁有抽象類的指針,以便於操作抽象類的各個派生類
- 虛函數派生下去的仍然為虛函數,而且可以省去 virtual 關鍵字
- 例子:
虛函數:
View Code class A //有虛函數的基類 { public: virtual void show(){cout<<"這是基類的 A !"<<endl;} //用虛函數聲明 show 函數 }; class B:public A { public: void show(){cout<<"這是派生類 B !"<<endl;} }; class C //沒有虛函數的基類 { public: void show(){cout<<"這是基類 C !"<<endl;} }; class D:public C { public: void show(){cout<<"這是派生類 D !"<<endl;} }; void func( ) { A* a = new B; C* c = new D; a->show(); //通過虛函數找到子類的成員函數 show c->show(); //沒有虛函數所以只能調用到基類的成員函數 show } /************************************ 調用 func 函數后的輸出結果: 這是派生類 B ! 這是基類 C ! ************************************/
純虛函數:
View Code class A //有純虛函數的基類 { public: virtual void show() = NULL; //用純虛函數聲明 show 函數 }; //要注意的是如果類有純虛函數那么這個類無法產生實例,並且純虛函數的必須在其的派生類中實現 class B:public A { public: void show(){cout<<"這是派生類 B !"<<endl;}//實現基類 A 的純虛函數 show };
利用虛函數/純虛函數,打破子類訪問權限.例子如下:
View Code #include<iostream> #include<cstdlib> using namespace std; class A{ public: virtual void output1() = 0; virtual void output2(){cout<<"this is A"<<endl;} }; class B:public A{ private: void output1(){cout<<"this is success!"<<endl;} protected: void output2(){cout<<"this is B"<<endl;} }; void main() { A* a; B b; a = &b; a->output1(); a->output2(); //通過 a 我們訪問到了 B 變量 b 的私有成員函數和保護成員函數 output1,output2 //b.output1(); //b.output2();//這里我們是無權限去訪問類 B 變量 b 的私有成員函數和保護成員函數 output1,output2 system("pause"); }
成員函數屬性:
函數 能否被繼承 成員還是友員 默認能否生成 能否為虛函數 是否可以有返回值
構造函數 × 成員 √ × ×
析構函數 × 成員 √ √ ×
= × 成員 √ √ √
& √ 任意 √ √ √
轉換函數 √ 成員 × √ ×
() √ 成員 × √ √
[ ] √ 成員 × √ √
→ √ 成員 × √ √
op = √ 任意 × √ √
new √ 靜態成員 × × void *
delete √ 靜態成員 × × void
其他操作符 √ 任意 × √ √
其他成員 √ 成員 × √ √
友元 × 友員 × × √
友元類:
聲明:
在類中用 friend class 友元類名;
作用:
在友元類的所有成員函數都可以訪問此類中的所有數據
格式:
- class 友元類名 ;//向前聲明本類
- class 擁有友元類的類名{....};//擁有友元類的類的定義
- class 友元類名 {…}//友元的定義
特點:
- 友元關系不能被繼承
- 友元關系是單向的,不具有交換性.若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的聲明.
- 友元關系不具有傳遞性.若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明
例子:
使用友元類:
View Code class B; //友元類的向前聲明 class A //擁有友元類的類的定義 { int y; public: A() { y=4; } friend B; }; class B //友元類的定義 { public: void show(A &a) { cout<<a.y<<endl; } };
多個類包含一個友元類:
View Code class C; //友元類的向前聲明 class A //擁有友元類的類的定義 { int x; public: A() { x=4; } friend C; }; class B //擁有友元類的類的定義 { int y; public: B() { y=6; } friend C; }; class C //友元類的定義 { public: void show(A &a,B &b) { cout<<a.x<<" "<<b.y<<endl; } }; void func() { A a; B b; C c; c.show(a,b); }
一個類包含多個個友元類:
View Code class B; //友元類向前聲明 class C; //友元類向前聲明 class A //擁有友元類的定義 { int x; public: A() { x=4; } friend C; friend B; }; class B //友元類的定義 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class B)"<<endl; } }; class C //友元類的定義 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class C)"<<endl; } }; void func() { A a; B b; C c; b.show(a); c.show(a); }