友元和继承(Friendship and inheritance)

重要的事情说三遍:

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

友元函数(Friend functions)

原则上,类的私有成员和保护成员不能从它们被声明的类之外访问。然而,这条规则对“友元”不适用。

友元是使用 friend 关键字声明的函数或类。

如果一个非成员函数被声明为某个类的友元函数,那么它可以访问该类的私有成员和保护成员。要实现这一点,只需在类中包含该外部函数的声明,并在前面加上 friend 关键字:

// 友元函数
#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 类型的不同对象的私有成员 widthheight。注意,在 duplicate 函数的声明以及它在 main 中的使用时,duplicate 函数都没有被视为 Rectangle 类的成员函数。它只是能够访问其私有和保护成员,而不是其成员函数。

友元函数的典型用例是执行涉及两个不同类的操作,并且需要访问这两个类的私有或保护成员。

友元类(Friend classes)

与友元函数类似,友元类是其成员能够访问另一个类的私有或保护成员的类:

// 友元类
#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 声明为友元。

友元关系是单向的,除非明确指定:在我们的例子中,RectangleSquare 视为友元类,但 Square 并没有被 Rectangle 视为友元。因此,Rectangle 的成员函数可以访问 Square 的私有和保护成员,但反之则不行。当然,如果需要的话,可以将 Square 也声明为 Rectangle 的友元,从而授予其访问权限。

友元关系不是传递的:除非明确指定,否则友元的友元不被视为友元。

类之间的继承

C++ 中的类可以扩展,创建新类,这些新类保留基类的特性。这一过程称为继承,涉及一个基类和一个派生类:派生类继承基类的成员,并可以添加自己的成员。

例如,假设我们有一系列类来描述两种多边形:矩形和三角形。这两种多边形具有某些共同的属性,例如计算它们面积所需的值:它们都可以简单地用高度和宽度(或基底)来描述。

这可以在类的世界中用一个 Polygon 类来表示,然后我们从中派生出其他两个类:RectangleTriangle

inheritance.jpg

Polygon 类将包含这两种多边形共有的成员。在我们的例子中:widthheightRectangleTriangle 将是它的派生类,具有不同于另一种多边形的特定特征。
从其他类派生的类,它继承基类的所有可访问成员。这意味着,如果基类包含成员 A,我们从它派生一个包含另一个成员 B 的类,则派生类将包含成员 A 和成员 B
两个类之间的继承关系在派生类中声明。派生类的定义使用以下语法:

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

其中 derived_class_name 是派生类的名称,base_class_name 是其基类的名称。public 访问说明符可以被其他任何一个访问说明符(protectedprivate)替代。这个访问说明符限制了从基类继承的成员的最大可访问级别:具有更高可访问级别的成员将以这个级别继承,而具有相同或更严格访问级别的成员将保持其在派生类中的限制级别。

// 派生类
#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;
}

RectangleTriangle 类的对象分别包含从 Polygon 继承的成员:widthheightset_values

Polygon 类中使用的 protected 访问说明符与 private 类似。它的唯一不同之处在于继承:当一个类继承另一个类时,派生类的成员可以访问从基类继承的保护成员,而不能访问其私有成员。

通过将 widthheight 声明为 protected 而不是 private,这些成员也可以从派生类 RectangleTriangle 中访问,而不仅仅是从 Polygon 的成员中访问。如果它们是 public,则可以从任何地方访问它们。

我们可以根据哪些函数可以访问它们来总结不同的访问类型,如下所示:

访问类型publicprotectedprivate
同一类的成员
派生类的成员
非成员

“非成员”表示从类外部的任何访问,例如从 main、从另一个类或从一个函数。

在上述例子中,RectangleTriangle 继承的成员具有与它们在基类 Polygon 中相同的访问权限:

Polygon::width           // 保护访问
Rectangle::width         // 保护访问

Polygon::set_values()    // 公共访问
Rectangle::set_values()  // 公共访问 

这是因为在每个派生类上使用 public 关键字声明了继承关系:

class Rectangle: public Polygon { /* ... */ }

冒号(:)后的 public 关键字表示从后面跟随的类(在这种情况下为 Polygon)继承的成员将在派生类(在这种情况下为 Rectangle)中具有的最大可访问级别。由于 public 是最高可访问级别,通过指定这个关键字,派生类将继承所有成员,并保持它们在基类中的访问级别。

使用 protected 时,基类的所有公共成员将作为 protected 成员在派生类中继承。相反,如果指定了最严格的访问级别(private),则所有基类成员将作为 private 成员继承。

例如,如果 daughter 是从 mother 派生的类,我们定义为:

class Daughter: protected Mother;

这将设置 protected 作为 DaughterMother 继承的成员的最低限制访问级别。也就是说,所有在 Mother 中为 public 的成员将在 Daughter 中变为 protected。当然,这不会限制 Daughter 声明其自己的公共成员。这个最低限制访问级别仅适用于从 Mother 继承的成员。

如果没有为继承指定访问级别,编译器会假定类使用 class 关键字声明时为 private,使用 struct 关键字声明时为 public

实际上,大多数 C++ 中的继承使用案例应使用公共继承。当基类需要其他访问级别时,它们通常可以更好地表示为成员变量。

基类继承了什么?

原则上,公开派生类继承基类的每个成员,除了:

  • 它的构造函数和析构函数
  • 它的赋值操作符成员(operator=)
  • 它的友元
  • 它的私有成员

尽管基类的构造函数和析构函数的访问权限不是直接继承的,但它们会被派生类的构造函数和析构函数自动调用。

除非另有规定,派生类的构造函数调用基类的默认构造函数(即,不带参数的构造函数)。可以使用初始化列表的语法调用基类的不同构造函数:

derived_constructor_name (parameters) : base_constructor_name (parameters) {...}

例如:

// 构造函数和派生类
#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 对象和创建新的 Son 对象时分别调用了哪个 Mother 的构造函数。这种差异是由于 DaughterSon 的不同构造函数声明造成的:

Daughter (int a)          // 未指定任何内容:调用默认构造函数
Son (int a) : Mother (a)  // 指定构造函数:调用这个特定构造函数

多重继承(Multiple inheritance)

一个类可以从多个类继承,只需在类的基类列表中指定多个基类,并用逗号分隔(即,冒号之后)。例如,如果程序中有一个专门用于在屏幕上打印的类 Output,并且我们希望类 RectangleTriangle 也继承它的成员以及 Polygon 的成员,可以这样写:

class Rectangle: public Polygon, public Output;
class Triangle: public Polygon, public Output;

这是完整的示例:

// 多重继承
#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
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值