友元和继承

友元函数

原则上,类的私有和保护成员在声明它们所在的类之外是无法访问的。然而,这个规则不影响友元

友元是使用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
目录