跳至内容

Inheritance

继承

面向对象编程的特征是通过继承鼓励代码的重用。一个新的类是从现有的类创建的,这个现有的类被称为基类。派生类可以使用基类的成员,但也可以修改和补充它们。

许多类型都是现有类型的变体。通常为每个类型开发新的代码是很繁琐的。此外,新代码会引入新的错误。派生类继承了基类的描述,因此无需重新开发和测试代码。继承关系是层次化的。

层次化是一种方法,可以复制所有元素及其多样性和复杂性。它引入了对象的分类。例如,元素的周期表中有气体。它们具有所有周期元素固有的属性。

惰性气体是下一个重要的子类。层次化的意思是,惰性气体如氩气是一种气体,而气体又是系统的一部分。这样的层次化使得容易解释惰性气体的行为。我们知道它们的原子包含质子和电子,这一点对所有其他元素都是相同的。

我们知道它们在室温下处于气态,就像所有气体一样。我们知道惰性气体子类没有任何气体与其他元素发生常规化学反应,这是所有惰性气体的属性。

考虑一个几何形状的继承示例。为了描述各种简单的形状(圆形、三角形、矩形、正方形等),最好的方法是创建一个基类(ADT),它是所有派生类的祖先。

让我们创建一个基类CShape,它只包含描述形状的最常见成员。这些成员描述了任何形状的特征属性——形状的类型和主要锚点坐标。

示例:

//--- The base class Shape
class CShape
  {
protected:
   int       m_type;                   // Shape type
   int       m_xpos;                   // X - coordinate of the base point
   int       m_ypos;                   // Y - coordinate of the base point
public:
             CShape(){m_type=0; m_xpos=0; m_ypos=0;} // constructor
   void      SetXPos(int x){m_xpos=x;} // set X
   void      SetYPos(int y){m_ypos=y;} // set Y
  };

接下来,从基类创建新的派生类,在这些类中我们将添加必要的字段,每个字段指定一个特定的类。对于圆形形状,需要添加一个包含半径值的成员。正方形形状的特点是边长值。因此,从基类CShape继承的派生类将如下声明:

//--- The derived class circle
class CCircle : public CShape        // After a colon we define the base class
  {                                    // from which inheritance is made
private:
   int             m_radius;           // circle radius

public:
                   CCircle(){m_type=1;}// constructor, type 1
  };

正方形形状类的声明类似:

//--- the derived class Square
class CSquare : public CShape        // After a colon we define the base class
  {                                    // from which inheritance is made
private:
   int            m_square_side;       // square side

public:
                  CSquare(){m_type=2;} // constructor, type 2
  };

需要注意的是,在创建对象时,首先调用基类构造函数,然后才调用派生类的构造函数。当对象被销毁时,首先调用派生类的destructor,然后才调用基类destructor。

因此,通过在基类中声明最通用的成员,我们可以在派生类中添加指定特定类的额外成员。继承允许创建可以多次重用的强大代码库。

从已存在的类创建派生类的语法如下:

class class_name :
          (public | protected | private) opt  base_class_name
  {
    class members declaration
  };

派生类的一个方面是其成员的可视性(开放性)。public、protected和private关键字用于指示基类成员对派生类可用程度。在派生类头文件中的冒号后面的public关键字表示基类CShape的protected和public成员应作为派生类CCircle的protected和public成员继承。

基类的private类成员对派生类不可用。公共继承还意味着派生类(CCircle和CSquare)是CShapes。也就是说,正方形(CSquare)是一个形状(CShape),但形状不一定必须是正方形。

派生类是基类的修改,它继承了基类的protected和public成员。基类的构造函数和destructor无法被继承。除了基类的成员外,派生类中还添加了新的成员。

派生类可以包括与基类不同的成员函数的实现。它与重载没有任何共同之处,当相同函数名称的含义可能因不同的签名而不同。

在受保护继承中,基类的public和protected成员成为派生类的protected成员。在私有继承中,基类的public和protected成员成为派生类的private成员。

在受保护和私有继承中,“派生类的对象是基类的对象”这一关系不成立。受保护和私有继承类型很少使用,每种类型都需要谨慎使用。

应该理解,继承类型(公共、受保护或私有)不会影响从派生类到基类层次结构中访问基类成员的方式。无论哪种类型的继承,只有用public和protected访问说明符声明的基类成员才能在派生类中可用。让我们在以下示例中考虑这一点:

#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Example class with a few access types                            |
//+------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- The private member is not available from derived classes
   int               m_member;
protected:           //--- The protected method is available from the base class and its derived classes
   int               Member(){return(m_member);}
public:              //--- Class constructor is available to all members of classes
                     CBaseClass(){m_member=5;return;};
private:             //--- A private method for assigning a value to m_member
   void              Member(int value) { m_member=value;};

  };
//+------------------------------------------------------------------+
//| Derived class with errors                                        |
//+------------------------------------------------------------------+
class CDerived: public CBaseClass // specification of public inheritance can be omitted, since it is default
  {
public:
   void Func() // In the derived class, define a function with calls to base class members
     {
      //--- An attempt to modify a private member of the base class
      m_member=0;        // Error, the private member of the base class is not available
      Member(0);         // Error, the private method of the base class is not available in derived classes
      //--- Reading the member of the base class
      Print(m_member);   // Error, the private member of the base class is not available
      Print(Member());   // No error, protected method is available from the base class and its derived classes
     }
  };

在上面的示例中,CBaseClass只有一个公共方法——构造函数。创建类对象时,构造函数会自动调用。因此,私有成员m_member和受保护的方法Member()无法从外部调用。但在公共继承的情况下,基类的Member()方法可以从派生类中访问。

在受保护继承的情况下,基类的所有具有public和protected访问的成员都变为受保护的。这意味着如果基类的公共数据成员和方法可以从外部访问,那么在受保护继承中,它们只能从派生类及其进一步派生类访问。

//+------------------------------------------------------------------+
//| Example class with a few access types                            |
//+------------------------------------------------------------------+
class CBaseMathClass
  {
private:             //--- The private member is not available from derived classes
   double            m_Pi;
public:              //--- Getting and setting a value for m_Pi
   void              SetPI(double v){m_Pi=v;return;};
   double            GetPI(){return m_Pi;};
public:              // The class constructor is available to all members
                     CBaseMathClass() {SetPI(3.14);  PrintFormat("%s",__FUNCTION__);};
  };
//+------------------------------------------------------------------+
//| A derived class, in which m_Pi cannot be modified                |
//+------------------------------------------------------------------+
class CProtectedChildClass: protected CBaseMathClass // Protected inheritance
  {
private:
   double            m_radius;
public:              //--- Public methods in the derived class
   void              SetRadius(double r){m_radius=r; return;};
   double            GetCircleLength(){return GetPI()*m_radius;};
  };
//+------------------------------------------------------------------+
//| Script starting function                                         |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- When creating a derived class, the constructor of the base class will be called automatically
   CProtectedChildClass pt;
//--- Specify radius
   pt.SetRadius(10);
   PrintFormat("Length=%G",pt.GetCircleLength());
//--- If we uncomment the line below, we will get an error at the stage of compilation, since SetPI() is now protected
// pt.SetPI(3);

//--- Now declare a variable of the base class and try to set the Pi constant equal to 10
   CBaseMathClass bc;
   bc.SetPI(10);
//--- Here is the result
   PrintFormat("bc.GetPI()=%G",bc.GetPI());
  }

示例表明,基类CBaseMathClass中的SetPI()和GetPi()方法对程序中的任何位置都是开放的且可调用。但同时,对于从它派生的CProtectedChildClass,这些方法只能从CProtectedChildClass类或其派生类中调用。

在私有继承的情况下,基类的所有具有public和protected访问的成员都变为私有的,在进一步的继承中无法调用它们。

MQL4没有多重继承。

另请参阅

结构和类

最后更新于