继承算法

OOP属性特点是通过继承算法鼓励代码重复使用。新类从现存的,基本类中生成。衍生类使用基本类成员,但是也做以更改和补充。

许多类型是现存类型的变异。为每个类开发新代码非常乏味。此外,新代码意味着新的错误。衍生类继承了基本类的描述,因此不必重复开发和重复测试代码。继承关系是层级体系。

层级是一个允许复制所有多样性和复杂性元素的类函数。它介绍了对象等级。例如,元素周期表有气体。它们对所有的周期元素控制固有的属性。

惰性气体是下一重要子集的 构成。层级就是惰性气体,例如氩是气体,是系统的一部分。这样的层级可以很轻松的解释惰性气体。我们知道其原子包括质子和电子,对于所有其他元素也是如此。

我们知道如其他气体一样它们在常温也是气体状态。我们知道惰性气体子集中,无气体能与其他化学元素发生化学反应,它是所有惰性气体的特性。

考虑到几何图形的继承例子。描述各种简单图形(圆形,三角形,矩形,正方形等等),最好的方法就是创建基本类(ADT),它是所有衍生类的先祖。

让我们创建一个基本类 CShape,它包括描述图形最常用的构件。这些构件描述任何图形所特有的属性-图像类型和定位点主坐标。

示例:

//--- 基本类的形状
class CShape{}
  {
protected:
   int       m_type;                   // 形状类型
   int       m_xpos;                   // 基本点的X - 坐标 
   int       m_ypos;                   // 基本点的Y - 坐标 
public:
             CShape(){m_type=0; m_xpos=0; m_ypos=0;} // constructor
   void      SetXPos(int x){m_xpos=x;} // 设置 X
   void      SetYPos(int y){m_ypos=y;} // 设置 Y
  };

下一步,创建基本类衍生出的新类,这里我们可以添加说明类的必要的字段。对于圆形添加包括半径值构件是必须的。正方形以边值为特点。因此,由继承基本类CShape而衍生的类如下声明:

//--- 派生类 圆形
class CCircle : public CShape        // 冒号后定义基本类
  {                                    // 从继承算法开始
private:
   int             m_radius;           // 圆弧半径
 
public:
                   CCircle(){m_type=1;}// 构造函数, 类型 1 
  };

正方形类声明类似:

//--- 派生类 方形
class CSquare : public CShape        // 冒号后定义基本类
  {                                    // 从继承算法开始
private:
   int            m_square_side;       // 方形的边
 
public:
                  CSquare(){m_type=2;} // 构造函数,类型2
  };

注意对象创建时首先调用基本类构造函数,然后调用衍生类的构造函数。当对象毁坏时首先调用衍生类的析构函数,然后调用基本类析构函数。

因此,通过声明基本类中常用构件,我们可以在衍生类中添加额外构件,指定特殊类。继承算法允许创建多次重复使用的强大的代码函数库。

从现存类创建衍生类的句法如下:

class class_name : 
          (public | protected | privateopt  base_class_name
  {                                    
    class members declaration
  };

衍生类一方面就是其构件的可见性(公开),继承人(继承)。关键字public, protected 和private用于指定范围,该范围中基本类构件也对衍生类有效。衍生类表头中冒号后的Public关键字表明基本类CShape的protected和public构件应该继承为衍生类CCircle的protected和public构件。

基本类的private类构件对衍生类无效。public继承也意味着衍生类 (CCircle and CSquare) 就是 CShapes。也就是,正方形(CSquare)是一个图形(CShape),但是图形却不一定就是正方形。

衍生类是基本类的变体,它继承了基本类的protected 和 public构件。基本类的构造函数和析构函数不能继承。除了基本类的构件,新构件也会添加进衍生类中。

不同于基本类,衍生类包括执行构件函数。与重载无共同点,同名函数的意思会因签名不同而不同。

在protected继承中,基本类的public和protected构件成为衍生类的protected构件。在private 继承中,基本类的public 和protected构件成为衍生类的private构件。

在protected 和 private 继承中, “衍生类对象就是基本类对象”的关系不是真的。 protected 和 private 继承类型也很少见,每一个都需要小心使用。

 

应该了解继承类型(public,protected或private)不会影响从衍生类访问继承层次结构的基类成员的方法。任何继承类型中,只有public 和 protected访问说明符声明的基类成员可用衍生类。让我们考虑一下下面的示例:

#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| 一些访问类型的示例类                                                |
//+------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- 从衍生类,private成员不可用
   int               m_member;
protected:           //--- 从基类及其衍生类,protected方法不可用
   int               Member(){return(m_member);}
public:              //--- 类构造函数可用于类的所有成员
                     CBaseClass(){m_member=5;return;};
private:             //--- 将值分配给m_member的private方法
   void              Member(int value) { m_member=value;};
 
  };
//+------------------------------------------------------------------+
//| 有错误的衍生类                                                     |
//+------------------------------------------------------------------+
class CDerived: public CBaseClass // 由于默认的原因,public继承规范可以忽略。
  {
public:
   void Func() // 在衍生类中,定义调用到基类成员的函数。
     {
      //--- 试图修改基类的private成员
      m_member=0;        // 错误,基类private成员不可用
      Member(0);         // 错误,基类的private方法不可用在衍生类
      //--- 阅读基类的成员
      Print(m_member);   // 错误,基类的private成员不可用
      Print(Member());   // 没有错误,protected方法可从基类及其衍生类使用
     }
  };

在上面的示例中,CBaseClass只有一个public方法――构造函数。当创建类对象时构造函数会自动调用。因此,private成员m_member和protected方法Member()不能从外部调用。但是如果是public继承类型,基类的Member()方法将可以从衍生类使用。

如果是protected继承类型的情况下,所有public和protected访问权限的基类成员都会成为protected。这就意味着如果public基类的数据成员和方法从外部访问,那么protected继承类型的情况下,他们只能从衍生类及其衍生品的类中使用。

//+------------------------------------------------------------------+
//| 一些访问类型的示例类                                                |
//+------------------------------------------------------------------+
class CBaseMathClass
  {
private:             //--- private成员不可从衍生类使用
   double            m_Pi;
public:              //--- 获取和设置m_Pi值
   void              SetPI(double v){m_Pi=v;return;};
   double            GetPI(){return m_Pi;};
public:              // 类构造函数可用于所有成员
                     CBaseMathClass() {SetPI(3.14);  PrintFormat("%s",__FUNCTION__);};
  };
//+------------------------------------------------------------------+
//| 一个衍生类,在此m_Pi不能修改                                         |
//+------------------------------------------------------------------+
class CProtectedChildClass: protected CBaseMathClass // Protected继承类型
  {
private:
   double            m_radius;
public:              //--- 衍生类中的Public方法
   void              SetRadius(double r){m_radius=r; return;};
   double            GetCircleLength(){return GetPI()*m_radius;};
  };
//+------------------------------------------------------------------+
//| 脚本启动函数                                                       |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 创建衍生类时,基类的构造函数将自动调用
   CProtectedChildClass pt;
//--- 指定半径
   pt.SetRadius(10);
   PrintFormat("Length=%G",pt.GetCircleLength());
//--- 如果评论下面的字符串,我们在编译阶段将得到一个错误,因为SetPi()现在是protected的类型
// pt.SetPI(3); 
 
//--- 现在声明基类变量,尝试将Pi常量设置等于10
   CBaseMathClass bc;
   bc.SetPI(10);
//--- 下面是结果
   PrintFormat("bc.GetPI()=%G",bc.GetPI());
  }

实例表明,基类CBaseMathClass的方法SetPI()和GetPi()是开放状态并可以从程序的任何地方进行调用。但与此同时,对于从其衍生的CProtectedChildClass来说,这些方法只能从CProtectedChildClass类或其衍生类的方法进行调用。

如果是private继承类型,所有public和protected访问权限的基类成员都会成为private,并且在进一步继承中无法进行调用。

MQL5没有多继承算法。

另见

架构和类