友元和繼承
友元函式
原則上,類的私有和保護成員在宣告它們所在的類之外是無法訪問的。然而,這個規則不影響
友元。
友元是使用
friend
關鍵字宣告的函式或類。
如果我們想將一個外部函式宣告為類的友元,從而允許該函式訪問類的私有和保護成員,我們可以在類內宣告該外部函式的原型,並在其前面加上關鍵字
friend:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
// friend functions
#include <iostream>
using namespace std;
class CRectangle {
int width, height;
public:
void set_values (int, int);
int area () {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();
return 0;
}
|
24 |
要放回的字元的
重複函式是...的友元
CRectangle。在該函式內部,我們可以訪問成員
寬度和
height來自不同型別的物件
CRectangle,這些是私有成員。請注意,在宣告
duplicate()中,或者在後續使用
main()我們沒有考慮
重複類的成員
CRectangle。它不是!它只是在不是成員的情況下訪問其私有和保護成員。
友元函式可以用於,例如,在兩個不同的類之間進行操作。通常,使用友元函式不符合面向物件程式設計的 منهج,因此,只要有可能,最好使用同一類的成員來執行操作。例如,在前面的例子中,將
duplicate()整合到類中
CRectangle.
友元類
正如我們可以定義友元函式一樣,我們也可以將一個類定義為另一個類的友元,授予第一個類訪問第二個類受保護和私有成員的許可權。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
// friend class
#include <iostream>
using namespace std;
class CSquare;
class CRectangle {
int width, height;
public:
int area ()
{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以便
CRectangle成員函式可以訪問...的受保護和私有成員
CSquare,更具體地說,訪問
CSquare::side,它描述了正方形的邊長。
你可能還會在程式開頭看到一些新東西:類的一個空宣告
CSquare。這是必需的,因為在宣告
CRectangle時,我們引用了 CSquare(在...中作為引數
convert())。的定義
CSquare稍後包含,所以如果我們不包含之前的空宣告
CSquare,這個類在宣告的定義中將不可見
CRectangle.
。請注意,朋友關係不是相互的,除非我們明確指定。在我們的例子中,
CRectangle被...視為友元類
CSquare,但
CRectangle不認為
CSquare是友元,所以
CRectangle可以訪問...的受保護和私有成員
CSquare,但反之則不行。當然,如果需要,我們也可以宣告
CSquare是...的友元
CRectangle。
友元關係的另一個特性是它們是
非傳遞的:朋友的朋友除非明確指定,否則不被認為是朋友。
類之間的繼承
C++類的關鍵特性是繼承。繼承允許建立從其他類派生的類,因此它們會自動包含其“父類”的某些成員,再加上自己的成員。例如,我們假設我們想宣告一系列描述多邊形的類,例如我們的
CRectangle,或者像
CTriangle。它們具有某些共同的屬性,例如都可以透過兩個邊來描述:高和底。
這可以在類的世界中用一個類來表示
CPolygon,從中我們將派生出另外兩個類
CRectangle和
CTriangle.
該類
CPolygon將包含兩種多邊形共有的成員。在我們的例子中
寬度和
height。和
CRectangle和
CTriangle將是它的派生類,具有從一種多邊形到另一種多邊形的不同特定特徵。
從其他類派生的類繼承了基類的所有可訪問成員。這意味著如果基類包含一個成員
A,我們將其派生到另一個帶有成員的類
B,派生類將同時包含成員
A和
B.
為了從一個類派生另一個類,我們在派生類的宣告中使用冒號(
:),使用以下格式
class derived_class_name: public base_class_name
{ /*...*/ };
其中
derived_class_name是派生類的名稱,而
base_class_name是它所基於的類的名稱。該
public訪問說明符可以被其他任何訪問說明符替換
protected和
private。此訪問說明符限制了從基類繼承的成員的可訪問級別:可訪問級別更高的成員將以該級別繼承,而訪問級別相等或更嚴格的成員將在派生類中保留其嚴格級別。
|
// derived classes
#include <iostream>
using namespace std;
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 ()
{ return (width * height); }
};
class CTriangle: public CPolygon {
public:
int area ()
{ 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。這些是
寬度,
height和
set_values().
要放回的字元的
protected訪問說明符類似於
private。它唯一的區別實際上發生在繼承方面。當一個類繼承自另一個類時,派生類的成員可以訪問從基類繼承的受保護成員,但不能訪問其私有成員。
因為我們想要
寬度和
height可從派生類的成員訪問
CRectangle和
CTriangle而不僅僅是透過...的成員
CPolygon,我們使用了
protected訪問而不是
private.
。我們可以按如下方式總結不同訪問型別根據誰可以訪問它們
訪問 | public | protected | private |
同一類的成員 | 是 | 是 | 是 |
派生類的成員 | 是 | 是 | 否 |
非成員 | 是 | 否 | 否 |
其中“非成員”代表任何來自類外部的訪問,例如來自
main(),來自另一個類或來自一個函式。
在我們的例子中,繼承的成員
CRectangle和
CTriangle具有與其基類中相同的訪問許可權
CPolygon:
1 2 3 4 5
|
CPolygon::width // protected access
CRectangle::width // protected access
CPolygon::set_values() // public access
CRectangle::set_values() // public access
|
這是因為我們使用了
public關鍵字在每個派生類上定義繼承關係
1
|
class CRectangle: public CPolygon { ... }
|
這個
public關鍵字在冒號(
:)之後表示從其後的類(在本例中為
CPolygon)繼承的成員的最可訪問級別。由於
public是最可訪問的級別,透過指定此關鍵字,派生類將以其在基類中的相同級別繼承所有成員。
如果我們指定更嚴格的訪問級別,例如
protected,基類的所有公共成員在派生類中被繼承為受保護。而如果我們指定所有訪問級別中最嚴格的
private,則所有基類成員都被私有繼承。
例如,如果
daughter是從...派生的類
mother我們定義為
1
|
class daughter: protected mother;
|
這將設定
protected作為...成員的最大可訪問級別
daughter它從...繼承的
mother。也就是說,在...中公共的所有成員將成為...中的受保護成員
mother。當然,這不會限制
daughter宣告其自己的公共成員。該最大可訪問級別僅針對從...繼承的成員設定
daughter如果未顯式指定任何訪問級別用於繼承,則編譯器會假定 classes declared with 的 private 關鍵字,而 struct classes declared with 的 public 關鍵字
mother.
class
類struct
關鍵字,以及 classes declared with 的 public 關鍵字.
從基類繼承了什麼?
原則上,派生類繼承基類的所有成員,除了
- 其建構函式和解構函式
- 其 operator=() 成員
- 其友元
雖然基類的建構函式和解構函式本身不被繼承,但在建立或銷燬派生類的新物件時,其預設建構函式(即沒有引數的建構函式)和解構函式總是會被呼叫。
如果基類沒有預設建構函式,或者您希望在建立新的派生物件時呼叫過載建構函式,您可以在派生類的每個建構函式定義中指定它
derived_constructor_name (parameters) : base_constructor_name (parameters) {...}
例如:
|
// constructors and derived classes
#include <iostream>
using namespace std;
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 (0);
son daniel(0);
return 0;
}
|
mother: no parameters
daughter: int parameter
mother: int parameter
son: int parameter |
請注意,當建立新的...物件時呼叫...的建構函式,而建立...物件時呼叫...的建構函式之間的區別。
motherchild
daughterparent
child區別在於...的建構函式宣告
daughter和
child:
1 2
|
daughter (int a) // nothing specified: call default
son (int a) : mother (a) // constructor specified: call this
|
多重繼承
在 C++ 中,一個類完全可以繼承一個以上的類。這可以透過在派生類宣告中用逗號分隔不同的基類來實現。例如,如果我們有一個用於在螢幕上列印的特定類(
COutput),並且我們希望我們的類
CRectangle和
CTriangle除了...的成員外,還繼承其成員
CPolygon我們可以寫
1 2
|
class CRectangle: public CPolygon, public COutput;
class CTriangle: public CPolygon, public COutput;
|
這是完整的示例
|
// multiple inheritance
#include <iostream>
using namespace std;
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 ()
{ return (width * height); }
};
class CTriangle: public CPolygon, public COutput {
public:
int area ()
{ 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 |