友元函數(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。
下表按照誰能訪問總結了不同訪問權限類型:
可以訪問 | public | protected | private |
---|---|---|---|
本class的成員 | yes | yes | yes |
子類的成員 | yes | yes | no |
非成員 | yes | no | no |
這裏"非成員"指從class以外的任何地方引用,例如從main()中,從其它的class中或從全域(global)或本地(local)的任何函數中。
在我們的例子中,CRectangle 和CTriangle 繼承的成員與基類CPolygon擁有同樣的訪問限制:
CPolygon::width // protected access CRectangle::width // protected access CPolygon::set_values() // public access CRectangle::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) // 沒有特別制定:調用默認constructor son (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 |