• 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
    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
    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
    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
    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
    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;

  • 相关阅读:
    音视频学习(十四)——rtsp详解
    69. Sqrt(x)x 的平方根
    【PHP】PHP7中的引用计数
    架构师之路八分布式系统下大流量限流与消峰的方案
    mysql的执行逻辑与日志
    ASEMI快恢复二极管ES8JC参数,ES8JC规格,ES8JC封装
    调度算法+等待/周转时间计算
    SpringBoot集成海康网络设备SDK
    Signing for ‘xxx‘ requires a development team.
    [100天算法】-最短无序连续子数组(day 66)
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/126903166