C++ 面向对象(三)—— 类之间的关系
友元函數(Friend functions)
在前面的章節中我們已經看到了對class的不同成員存在3個層次的內部保護:public, protected 和 private。在成員為 protected 和 private的情況下,它們不能夠被從所在的class以外的部分引用。然而,這個規則可以通過在一個class中使用關鍵字friend來繞過,這樣我們可以允許一個外部函數獲得訪問class的protected 和 private 成員的能力。
為了實現允許一個外部函數訪問class的private 和 protected 成員,我們必須在class內部用關鍵字friend來聲明該外部函數的原型,以指定允許該函數共享class的成員。在下面的例子中我們聲明了一個 friend 函數 duplicate:
| // friend functions#include <iostream.h>class CRectangle {int width, height;public:void set_values (int, int);int area (void) {return (width * height);}friend CRectangle duplicate (CRectangle);};void CRectangle::set_values (int a, int b) {width = a;height = b;}CRectangle duplicate (CRectangle rectparam) {CRectangle rectres;rectres.width = rectparam.width*2;rectres.height = rectparam.height*2;return (rectres);}int main () {CRectangle rect, rectb;rect.set_values (2,3);rectb = duplicate (rect);cout << rectb.area();} | 24 |
函數duplicate是CRectangle的friend,因此在該函數之內,我們可以訪問CRectangle 類型的各個object的成員 width 和 height。注意,在 duplicate()的聲明中,及其在后面main()里被調用的時候,我們并沒有把duplicate 當作class CRectangle的成員,它不是。
friend 函數可以被用來實現兩個不同class之間的操作。廣義來說,使用friend 函數是面向對象編程之外的方法,因此,如果可能,應盡量使用class的成員函數來完成這些操作。比如在以上的例子中,將函數duplicate() 集成在class CRectangle 可以使程序更短。
友元類 (Friend classes)
就像我們可以定義一個friend 函數,我們也可以定義一個class是另一個的friend,以便允許第二個class訪問第一個class的 protected 和 private 成員。
| // friend class#include <iostream.h>class CSquare;class CRectangle {int width, height;public:int area (void) {return (width * height);}void convert (CSquare a);};Class CSquare {private:int side;public:void set_side (int a){side=a;}friend class CRectangle;};void CRectangle::convert (CSquare a) {width = a.side;height = a.side;}int main () {CSquare sqr;CRectangle rect;sqr.set_side(4);rect.convert(sqr);cout << rect.area();return 0;} | 16 |
在這個例子中,我們聲明了CRectangle 是CSquare 的friend,因此CRectangle可以訪問CSquare 的protected 和 private 成員,更具體地說,可以訪問CSquare::side,它定義了正方形的邊長。
在上面程序的第一個語句里你可能也看到了一些新的東西,就是class CSquare空原型。這是必需的,因為在CRectangle 的聲明中我們引用了CSquare (作為convert()的參數)。CSquare 的定義在CRectangle的后面,因此如果我們沒有在這個class之前包含一個CSquare 的聲明,它在CRectangle中就是不可見的。
這里要考慮到,如果沒有特別指明,友元關系(friendships)并不是相互的。在我們的CSquare 例子中,CRectangle 是一個friend類,但因為CRectangle 并沒有對CSquare作相應的聲明,因此CRectangle 可以訪問CSquare 的 protected 和private 成員,但反過來并不行,除非我們將 CSquare 也定義為CRectangle的 friend。
類之間的繼承(Inheritance between classes)
類的一個重要特征是繼承,這使得我們可以基于一個類生成另一個類的對象,以便使后者擁有前者的某些成員,再加上它自己的一些成員。例如,假設我們要聲明一系列類型的多邊形,比如長方形CRectangle或三角形CTriangle。它們有一些共同的特征,比如都可以只用兩條邊來描述:高(height)和底(base)。
這個特點可以用一個類CPolygon 來表示,基于這個類我們可以引申出上面提到的兩個類CRectangle 和 CTriangle 。
類CPolygon 包含所有多邊形共有的成員。在我們的例子里就是: width 和 height。而CRectangle 和 CTriangle 將為它的子類(derived classes)。
由其它類引申而來的子類繼承基類的所有可視成員,意思是說,如果一個基類包含成員A ,而我們將它引申為另一個包含成員B的類,則這個子類將同時包含 A 和 B。
要定義一個類的子類,我們必須在子類的聲明中使用冒號(colon)操作符: ,如下所示:
class derived_class_name: public base_class_name;這里derived_class_name 為子類(derived class)名稱,base_class_name 為基類(base class)名稱。public 也可以根據需要換為protected 或 private,描述了被繼承的成員的訪問權限,我們在以下例子后會很快看到:
| // derived classes#include <iostream.h>Class CPolygon {protected:int width, height;public:void set_values (int a, int b) { width=a; height=b;}};class CRectangle: public CPolygon {public:int area (void){ return (width * height); }};class CTriangle: public CPolygon {public:int area (void){ return (width * height / 2); }};int main () {CRectangle rect;CTriangle trgl;rect.set_values (4,5);trgl.set_values (4,5);cout << rect.area() << endl;cout << trgl.area() << endl;return 0;} | 20 10 |
如上所示,類 CRectangle 和 CTriangle 的每一個對象都包含CPolygon的成員,即: width, height 和 set_values()。
標識符protected 與 private類似,它們的唯一區別在繼承時才表現出來。當定義一個子類的時候,基類的protected 成員可以被子類的其它成員所使用,然而private 成員就不可以。因為我們希望CPolygon的成員width 和 height能夠被子類CRectangle 和 CTriangle 的成員所訪問,而不只是被CPolygon自身的成員操作,我們使用了protected 訪問權限,而不是 private。
下表按照誰能訪問總結了不同訪問權限類型:
| 本class的成員 | yes | yes | yes |
| 子類的成員 | yes | yes | no |
| 非成員 | yes | no | no |
這里"非成員"指從class以外的任何地方引用,例如從main()中,從其它的class中或從全域(global)或本地(local)的任何函數中。
在我們的例子中,CRectangle 和CTriangle 繼承的成員與基類CPolygon擁有同樣的訪問限制:
CPolygon::width // protected accessCRectangle::width // protected accessCPolygon::set_values() // public accessCRectangle::set_values() // public access這是因為我們在繼承的時候使用的是public,記得我們用的是:
class CRectangle: public CPolygon;這里關鍵字 public 表示新的類(CRectangle)從基類(CPolygon)所繼承的成員必須獲得最低程度保護。這種被繼承成員的訪問限制的最低程度可以通過使用 protected 或 private而不是public來改變。例如,daughter 是mother 的一個子類,我們可以這樣定義:
class daughter: protected mother;這將使得protected 成為daughter 從mother處繼承的成員的最低訪問限制。也就是說,原來mother 中的所有public 成員到daughter 中將會成為protected 成員,這是它們能夠被繼承的最低訪問限制。當然這并不是限制daughter 不能有它自己的public 成員。最低訪問權限限制只是建立在從mother中 繼承的成員上的。
最常用的繼承限制除了public 外就是private ,它被用來將基類完全封裝起來,因為在這種情況下,除了子類自身外,其它任何程序都不能訪問那些從基類繼承而來的成員。不過大多數情況下繼承都是使用public的。
如果沒有明確寫出訪問限制,所有由關鍵字class 生成的類被默認為private ,而所有由關鍵字struct 生成的類被默認為public。
什么是從基類中繼承的? (What is inherited from the base class?)
理論上說,子類(drived class)繼承了基類(base class)的所有成員,除了:
- 構造函數Constructor 和析構函數destructor
- operator=() 成員
- friends
雖然基類的構造函數和析構函數沒有被繼承,但是當一個子類的object被生成或銷毀的時候,其基類的默認構造函數 (即,沒有任何參數的構造函數)和析構函數總是被自動調用的。
如果基類沒有默認構造函數,或你希望當子類生成新的object時,基類的某個重載的構造函數被調用,你需要在子類的每一個構造函數的定義中指定它:
derived_class_name (parameters) : base_class_name (parameters) {}例如 (注意程序中黑體的部分):
| // constructors and derivated classes#include <iostream.h>class mother {public:mother (){ cout << "mother: no parameters\n"; }mother (int a){ cout << "mother: int parameter\n"; }};class daughter : public mother {public:daughter (int a){ cout << "daughter: int parameter\n\n"; }};class son : public mother {public:son (int a) : mother (a){ cout << "son: int parameter\n\n"; }};int main () {daughter cynthia (1);son daniel(1);return 0;} | mother: no parameters daughter: int parameter mother: int parameter son: int parameter |
觀察當一個新的daughter object生成的時候mother的哪一個構造函數被調用了,而當新的son object生成的時候,又是哪一個被調用了。不同的構造函數被調用是因為daughter 和 son的構造函數的定義不同:
daughter (int a) // 沒有特別制定:調用默認constructorson (int a) : mother (a) // 指定了constructor: 調用被指定的構造函數多重繼承(Multiple inheritance)
在C++ 中,一個class可以從多個class中繼承屬性或函數,只需要在子類的聲明中用逗號將不同基類分開就可以了。例如,如果我們有一個特殊的class COutput 可以實現向屏幕打印的功能,我們同時希望我們的類CRectangle 和 CTriangle 在CPolygon 之外還繼承一些其它的成員,我們可以這樣寫:
class CRectangle: public CPolygon, public COutput {class CTriangle: public CPolygon, public COutput {
以下是一個完整的例子:
| // multiple inheritance#include <iostream.h>class CPolygon {protected:int width, height;public:void set_values (int a, int b){ width=a; height=b;}};class COutput {public:void output (int i);};void COutput::output (int i) {cout << i << endl;}class CRectangle: public CPolygon, public COutput {public:int area (void){ return (width * height); }};class CTriangle: public CPolygon, public COutput {public:int area (void){ return (width * height / 2); }};int main () {CRectangle rect;CTriangle trgl;rect.set_values (4,5);trgl.set_values (4,5);rect.output (rect.area());trgl.output (trgl.area());return 0;} | 20 10 |
總結
以上是生活随笔為你收集整理的C++ 面向对象(三)—— 类之间的关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 内核同步机制
- 下一篇: C++ 高级数据类型(三)—— 指针