C++ Tutorials: C++ Language: Classes: Friendship and inheritance

C++官方参考链接:Friendship and inheritance - C++ Tutorials (cplusplus.com)

友元和继承
友元函数
原则上,类的私有(private)成员和受保护(protected)成员不能从声明它们的同一个类的外部访问。然而,这条规则不适用于“友元”。
友元是用friend关键字声明的函数或类。
非成员函数可以访问类的私有(private)成员和受保(protected)成员,如果它被声明为类的友元这是通过在类中包含这个外部函数的声明,并在它之前加上关键字friend来实现的:
// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;

duplicate函数是Rectangle类的友元。因此,函数duplicate能够访问Rectangle类型的不同对象的成员width和height(它们是私有的)。请注意,无论是duplicate的声明还是其后在main中的使用,都没有将函数duplicate视为Rectangle类的成员。它不是!它只需访问其私有和受保护的成员,而无需成为成员。
友元函数的典型用例是在两个不同的类之间执行的操作,这些操作访问两个类的私有(private)成员或受保护(protected)成员。

友元类
类似于友元函数,友元类是一个类,其成员可以访问另一个类的私有(private)或受保护(protected)成员:

// friend class
#include <iostream>
using namespace std;

class Square;

class Rectangle {
    int width, height;
  public:
    int area ()
      {return (width * height);}
    void convert (Square a);
};

class Square {
  friend class Rectangle;
  private:
    int side;
  public:
    Square (int a) : side(a) {}
};

void Rectangle::convert (Square a) {
  width = a.side;
  height = a.side;
}
  
int main () {
  Rectangle rect;
  Square sqr (4);
  rect.convert(sqr);
  cout << rect.area();
  return 0;
}

在本例中,类Rectangle是类Square的友元,允许Rectangle的成员函数访问Square的私有成员和受保护成员。更具体地说,Rectangle访问成员变量Square::side,该变量描述正方形的边。
这个例子中还有一些新的东西:在程序的开始,类Square的声明是空的。这是必要的,因为类Rectangle使用Square(作为成员convert中的形参),而Square使用Rectangle(声明它为友元)。
除非指定,否则友元永远不会相互对应:在我们的示例中,Rectangle被Square视为友元类,但Square不被Rectangle视为友元类。因此,Rectangle的成员函数可以访问Square的受保护成员和私有成员,但反之则不行。当然,如果需要,Square也可以被声明为Rectangle的友元,授予这样的访问权限。
友元的另一个特点是它们不能传递:友元的友元不被认为是友元,除非明确指定。

类之间的继承
C++中的类可以扩展,创建保留基类特征的新类。这个过程称为继承,涉及一个基类和一个派生类:派生类继承基类的成员,在基类的基础上添加自己的成员。
例如,让我们想象有一系列的类来描述两种多边形:矩形和三角形。这两个多边形具有某些共同的属性,例如计算它们的面积所需的值:它们都可以简单地用高和宽(或基)来描述。
这可以在类的世界中用一个类Polygon来表示,我们将从中派生出另外两个类:Rectangle和Triangle:

Polygon类将包含对这两种类型的多边形都通用的成员。在我们的例子中:width和height。Rectangle和Triangle是它的派生类,具有不同类型多边形的特定特征。
派生自其他类的类继承基类的所有可访问成员。这意味着,如果基类包含成员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 Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b;}
 };

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
 };

class Triangle: public Polygon {
  public:
    int area ()
      { return width * height / 2; }
  };
  
int main () {
  Rectangle rect;
  Triangle trgl;
  rect.set_values (4,5);
  trgl.set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;

Rectangle和Triangle类的对象都包含从Polygon继承的成员。它们是:width,height和set_values。
Polygon类中使用的protected访问说明符类似于private。它唯一的区别实际上发生在继承方面:当一个类继承另一个类时,派生类的成员可以访问从基类继承的protected成员,但不能访问其private成员。
通过将width和height声明为protected而非private,这些成员也可以从派生类Rectangle和Triangle中访问,而不仅仅是从Polygon的成员中访问。如果它们是public的,就可以从任何地方访问。
我们可以根据函数的访问方式总结不同的访问类型,如下所示:

Access(访问级别)publicprotectedprivate
members of the same class(同一个类的成员)yesyesyes
members of derived class(派生类的成员)yesyesno
not members(非成员)yesnono

其中“not members”表示来自类外部的任何访问,如来自main、来自另一个类或来自函数。
在上面的例子中,Rectangle和Triangle继承的成员具有与它们在基类Polygon中相同的访问权限:
Polygon::width           // protected access
Rectangle::width         // protected access

Polygon::set_values()    // public access
Rectangle::set_values()  // public access 
这是因为继承关系已经在每个派生类上使用public关键字声明了:
class Rectangle: public Polygon { /* ... */ }
冒号后面的此public关键字 (:)表示从它后面的类(在本例中为 Polygon)继承的成员将从派生类(在本例中为Rectangle)继承的最高访问的级别。由于public是最高访问的级别,通过指定此关键字,派生类将继承所有具有与基类中相同级别的成员。 
使用protected时,基类的所有公有成员都被继承为派生类中的protected成员。相反,如果指定了限制最大的访问级别(private),则所有基类成员都继承为private
例如,如果女儿是从母亲派生的类,我们将其定义为:
class Daughter: protected Mother;
这将把protected设置为从Mother继承的Daughter成员的限制性较低的访问级别。也就是说,所有在Mother中public成员将在Daughter中变为protected。当然,这并不会限制Daughter声明自己的public的成员。限制较低的访问级别仅为继承自Mother的成员设置。 
如果没有为继承指定访问级别,编译器假定用关键字class声明的类是private的,而用struct声明的类是public的。
实际上,C++中继承的大多数用例都应该使用公有继承。当基类需要其他访问级别时,它们通常可以更好地表示为成员变量。 

从基类继承了什么?
原则上,公有派生类继承对基类的每个成员的访问权,除了:
*它的构造函数和析构函数
*它的赋值操作符成员(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 kelly(0);
  Son bud(0);
  
  return 0;

请注意在创建一个新的Daughter对象时调用那个Mother的构造函数与当它是一个Son对象时调用哪个Mother的构造函数之间的区别。这种差异是由于Daughter和Son的构造函数声明不同造成的:
Daughter (int a)          // nothing specified: call default constructor
Son (int a) : Mother (a)  // constructor specified: call this specific constructor 

多重继承
一个类可以从多个类继承,只需在类的基类列表中指定多个用逗号分隔的基类(例如,在冒号之后)。例如,如果程序有一个名为Output的特定类要打印到屏幕上,并且我们希望我们的类Rectangle和Triangle除了继承Polygon的成员外,也继承它的成员,那么我们可以这样写:
class Rectangle: public Polygon, public Output;
class Triangle: public Polygon, public Output;
下面是完整的例子:
// multiple inheritance
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
};

class Output {
  public:
    static void print (int i);
};

void Output::print (int i) {
  cout << i << '\n';
}

class Rectangle: public Polygon, public Output {
  public:
    Rectangle (int a, int b) : Polygon(a,b) {}
    int area ()
      { return width*height; }
};

class Triangle: public Polygon, public Output {
  public:
    Triangle (int a, int b) : Polygon(a,b) {}
    int area ()
      { return width*height/2; }
};
  
int main () {
  Rectangle rect (4,5);
  Triangle trgl (4,5);
  rect.print (rect.area());
  Triangle::print (trgl.area());
  return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值