友元和繼承

友元函式

原則上,類的私有和保護成員在宣告它們所在的類之外是無法訪問的。然而,這個規則不影響友元

友元是使用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,從中我們將派生出另外兩個類CRectangleCTriangle.


該類CPolygon將包含兩種多邊形共有的成員。在我們的例子中寬度height。和CRectangleCTriangle將是它的派生類,具有從一種多邊形到另一種多邊形的不同特定特徵。

從其他類派生的類繼承了基類的所有可訪問成員。這意味著如果基類包含一個成員A,我們將其派生到另一個帶有成員的類B,派生類將同時包含成員AB.

為了從一個類派生另一個類,我們在派生類的宣告中使用冒號(:),使用以下格式

class derived_class_name: public base_class_name
{ /*...*/ };

其中derived_class_name是派生類的名稱,而base_class_name是它所基於的類的名稱。該public訪問說明符可以被其他任何訪問說明符替換protectedprivate。此訪問說明符限制了從基類繼承的成員的可訪問級別:可訪問級別更高的成員將以該級別繼承,而訪問級別相等或更嚴格的成員將在派生類中保留其嚴格級別。

// 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

類的物件CRectangleCTriangle每個都包含來自...的繼承成員CPolygon。這些是寬度, heightset_values().

要放回的字元的protected訪問說明符類似於private。它唯一的區別實際上發生在繼承方面。當一個類繼承自另一個類時,派生類的成員可以訪問從基類繼承的受保護成員,但不能訪問其私有成員。

因為我們想要寬度height可從派生類的成員訪問CRectangleCTriangle而不僅僅是透過...的成員CPolygon,我們使用了protected訪問而不是private.

。我們可以按如下方式總結不同訪問型別根據誰可以訪問它們

訪問publicprotectedprivate
同一類的成員
派生類的成員
非成員

其中“非成員”代表任何來自類外部的訪問,例如來自main(),來自另一個類或來自一個函式。

在我們的例子中,繼承的成員CRectangleCTriangle具有與其基類中相同的訪問許可權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.

classstruct關鍵字,以及 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

請注意,當建立新的...物件時呼叫...的建構函式,而建立...物件時呼叫...的建構函式之間的區別。motherchilddaughterparentchild區別在於...的建構函式宣告daughterchild:

1
2
daughter (int a)          // nothing specified: call default
son (int a) : mother (a)  // constructor specified: call this 

多重繼承

在 C++ 中,一個類完全可以繼承一個以上的類。這可以透過在派生類宣告中用逗號分隔不同的基類來實現。例如,如果我們有一個用於在螢幕上列印的特定類(COutput),並且我們希望我們的類CRectangleCTriangle除了...的成員外,還繼承其成員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  
Index
目錄