MQL4 Virtual Functions

Overview

  • **Purpose:** Provides a mechanism to dynamically select an appropriate function-member at runtime among functions of basic and derived classes.
  • **Keyword:** virtual is used as a function specifier.
  • **Applicability:** Can be used to change declarations for function-members only. Structures cannot have virtual functions.
  • **Function Body:** A virtual function must have an executable body.
  • **Invocation:** When called, its semantic is the same as other functions.
  • **Overriding:** A virtual function may be overridden in a derived class.
  • **Selection:** The choice of function definition to call is made dynamically at runtime, based on the actual type of the object, not the type of the pointer.
  • **Default Behavior:** If no member of a derived type exists, the virtual function of the base class is used by default.
  • **Destructors:** Destructors are always virtual, regardless of explicit declaration.
  • **Caution:** Calling virtual methods from constructors and destructors is not recommended; the result is undefined.
  • Example Scenario (Tetris)

  • **Base Class:** CTetrisShape contains a virtual function Draw().
  • **Derived Classes:** Classes like CTetrisShape1 and CTetrisShape6 provide their own implementations of the Draw() function.
  • **Dynamic Dispatch:** In CTetrisField::NewShape(), objects of derived classes are created and assigned to a base class pointer (m_shape). Calling m_shape.Draw() invokes the specific Draw() implementation of the actual object's derived class at runtime.
  • Code Snippets

    **Base Class CTetrisShape:**

    class CTetrisShape {
    protected:
      int m_type, m_xpos, m_ypos, m_xsize, m_ysize, m_prev_turn, m_turn, m_right_border;
    public:
      CTetrisShape();
      void SetRightBorder(int border) { m_right_border=border; }
      void SetYPos(int ypos) { m_ypos=ypos; }
      void SetXPos(int xpos) { m_xpos=xpos; }
      int GetYPos() { return(m_ypos); }
      int GetXPos() { return(m_xpos); }
      int GetYSize() { return(m_ysize); }
      int GetXSize() { return(m_xsize); }
      int GetType() { return(m_type); }
      void Left() { m_xpos-=SHAPE_SIZE; }
      void Right() { m_xpos+=SHAPE_SIZE; }
      void Rotate() { m_prev_turn=m_turn; if(++m_turn>3) m_turn=0; }
      virtual void Draw() { return; }
      virtual bool CheckDown(int& pad_array[]);
      virtual bool CheckLeft(int& side_row[]);
      virtual bool CheckRight(int& side_row[]);
    };
    

    **Derived Class CTetrisShape1:**

    class CTetrisShape1 : public CTetrisShape {
    public:
      virtual void Draw() {
        int i;
        string name;
        if(m_turn==0 || m_turn==2) {
          // horizontal
          for(i=0; i<4; i++) {
            name=SHAPE_NAME+(string)i;
            ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xpos+i*SHAPE_SIZE);
            ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ypos);
          }
        } else {
          // vertical
          for(i=0; i<4; i++) {
            name=SHAPE_NAME+(string)i;
            ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xpos);
            ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ypos+i*SHAPE_SIZE);
          }
        }
      }
    };
    

    **Derived Class CTetrisShape6:**

    class CTetrisShape6 : public CTetrisShape {
    public:
      virtual void Draw() {
        int i;
        string name;
        for(i=0; i<2; i++) {
          name=SHAPE_NAME+(string)i;
          ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xpos+i*SHAPE_SIZE);
          ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ypos);
        }
        for(i=2; i<4; i++) {
          name=SHAPE_NAME+(string)i;
          ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xpos+(i-2)*SHAPE_SIZE);
          ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ypos+SHAPE_SIZE);
        }
      }
    };
    

    **Object Creation and Call:**

    void CTetrisField::NewShape() {
      int nshape=rand()%7;
      switch(nshape) {
        case 0: m_shape=new CTetrisShape1; break;
        case 1: m_shape=new CTetrisShape2; break;
        case 2: m_shape=new CTetrisShape3; break;
        case 3: m_shape=new CTetrisShape4; break;
        case 4: m_shape=new CTetrisShape5; break;
        case 5: m_shape=new CTetrisShape6; break;
        case 6: m_shape=new CTetrisShape7; break;
      }
      m_shape.Draw(); // Dynamic dispatch occurs here
    }