用户定义类型

在C++中,关键词typedef允许创建用户定义数据类型。 因此,只需为现有的数据类型指定一个新的数据类型名称即可。不可以创建新的数据类型。而是用现有类型的新名称来替代。用户定义类型可以使应用程序更具灵活性:有时,使用替换宏(#define)就足以改变 typedef 指令。用户定义类型还可以提高代码的可读性,因为可以使用typedef将自定义名称应用于标准数据类型。创建用户类型条目的一般格式:

   typedef type new_name;

在这里,type 表示任何可接受的数据类型,而new_name是该类型的新名称。设置的新名称只能作为现有类型名称的补充(而不能替换它)。MQL5 允许使用typedef创建函数指针。

函数指针

函数指针通常根据以下格式定义

   typedef function_result_type (*Function_name_type)(list_of_input_parameters_types);

typedef之后,设置函数签名(输入参数的数字和类型,以及函数返回结果的类型)。下面就是创建和应用函数指针的简单示例:

//--- 声明接受两种int参数的函数指针

   typedef int (*TFunc)(int,int);

//--- TFunc 是一种类型,可以声明变量函数指针

   TFunc func_ptr; // pointer to the function

//--- 声明符合TFunc 描述的函数

   int sub(int x,int y) { return(x-y); }  // 从一个数减去一个数

   int add(int x,int y) { return(x+y); }  // 两个数字相加

   int neg(int x)       { return(~x);  }  // 逆位变量

//--- func_ptr 变量可以存储函数地址以便稍后声明

   func_ptr=sub;

   Print(func_ptr(10,5));

   func_ptr=add;

   Print(func_ptr(10,5));

   func_ptr=neg;           // 错误:neg 没有 int (int,int) 类型

   Print(func_ptr(10));    // 错误:需要两个参数

在该示例中,func_ptr变量可以接收subadd函数,因为它们具有如TFunc函数指针定义的每个int类型的两个输入参数。 相反,因为签名不同,所以neg函数不可以安排到func_ptr指针。

在用户界面安排事件模式

函数指针允许您创建用户界面时轻松创建事件处理。让我们通过CButton版块的示例来说明如何创建按键和添加函数来处理它们。首先,定义一个通过按下按键来调用的TAction函数并根据TAction描述创建三个函数。

//--- 创建一个自定义函数类型

typedef int(*TAction)(string,int);

//+------------------------------------------------------------------+

//|  打开文件                                                         |

//+------------------------------------------------------------------+

int Open(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(1);

  }

//+------------------------------------------------------------------+

//|  保存文件                                                         |

//+------------------------------------------------------------------+

int Save(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(2);

  }

//+------------------------------------------------------------------+

//|  关闭文件                                                         |

//+------------------------------------------------------------------+

int Close(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(3);

  }

 

然后,创建来自CButton的MyButton类,在这里我们应该添加TAction函数指针。

//+------------------------------------------------------------------+

//| 通过事件处理函数创建按键类                                           |

//+------------------------------------------------------------------+

class MyButton: public CButton

  {

private:

   TAction           m_action;                    // 图表事件处理程序

public:

                     MyButton(void){}

                    ~MyButton(void){}

   //--- 构造函数指定按键文本和事件处理函数指针 

                     MyButton(string text, TAction act)

     {

      Text(text);

      m_action=act;

     }

   //--- 设置OnEvent()事件处理程序调用的自定义函数

   void              SetAction(TAction act){m_action=act;}

   //--- 标准图表事件处理程序

   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override

     {

      if(m_action!=NULL && lparam==Id())

        {

         //--- 调用自定义 m_action() 处理程序 

         m_action(sparam,(int)lparam);

         return(true);

        }

      else

      //--- 返回调用CButton父类处理程序的结果

         return(CButton::OnEvent(id,lparam,dparam,sparam));

     }

  };

创建来自CAppDialog的CControlsDialog衍生类,为其添加m_buttons数组来存储MyButton类型的按键,以及 AddButton(MyButton &button)CreateButtons() 方法。

//+------------------------------------------------------------------+

//| CControlsDialog 类                                                |

//| 目标:管理应用程序的图形面板                                          |

//+------------------------------------------------------------------+

class CControlsDialog : public CAppDialog

  {

private:

   CArrayObj         m_buttons;                     // 按键数组

public:

                     CControlsDialog(void){};

                    ~CControlsDialog(void){};

   //--- 创建

   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) override;

   //--- 添加按键

   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};

protected:

   //--- 创建按键 

   bool              CreateButtons(void);

  };

//+------------------------------------------------------------------+

//| 在图表上创建CControlsDialog 对象                                   |

//+------------------------------------------------------------------+

bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)

  {

   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))

      return(false);

   return(CreateButtons());

//---

  }

//+------------------------------------------------------------------+

//| 定义                                                              |

//+------------------------------------------------------------------+

//--- 缩进和间隔 

#define INDENT_LEFT                         (11)      // 从左缩进(预留边框宽度)

#define INDENT_TOP                          (11)      // 从上缩进(预留边框宽度)

#define CONTROLS_GAP_X                      (5)       // X坐标间隔

#define CONTROLS_GAP_Y                      (5)       // Y坐标间隔

//--- 按键

#define BUTTON_WIDTH                        (100)     // X坐标大小

#define BUTTON_HEIGHT                       (20)      // Y坐标大小

//--- 显示区域

#define EDIT_HEIGHT                         (20)      // Y坐标大小

//+------------------------------------------------------------------+

//| 创建和添加按键到CControlsDialog面板                                 |

//+------------------------------------------------------------------+

bool CControlsDialog::CreateButtons(void)

  {

//--- 计算按键坐标

   int x1=INDENT_LEFT;

   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);

   int x2;

   int y2=y1+BUTTON_HEIGHT;

//--- 添加带有函数指针的按键对象

   AddButton(new MyButton("Open",Open));

   AddButton(new MyButton("Save",Save));

   AddButton(new MyButton("Close",Close));

//--- 创建图形按键

   for(int i=0;i<m_buttons.Total();i++)

     {

      MyButton *b=(MyButton*)m_buttons.At(i);

      x1=INDENT_LEFT+i*(BUTTON_WIDTH+CONTROLS_GAP_X);

      x2=x1+BUTTON_WIDTH;

      if(!b.Create(m_chart_id,m_name+"bt"+b.Text(),m_subwin,x1,y1,x2,y2))

        {

         PrintFormat("Failed to create button %s %d",b.Text(),i);

         return(false);

        }

      //--- 添加每个按键到CControlsDialog 集合

      if(!Add(b))

         return(false);

     }

//--- 成功

   return(true);

  }

现在,我们可以使用包括3个按键(打开,保存和关闭)的CControlsDialog控制面板开发程序。当点击按键时,以TAction指针形式调用合适的函数。

//--- 声明全局对象以便在启动程序时自动创建

CControlsDialog MyDialog;

//+------------------------------------------------------------------+

//| EA交易初始化函数                                                   |

//+------------------------------------------------------------------+

int OnInit()

  {

//--- 现在,在图表上创建对象

   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))

      return(INIT_FAILED);

//--- 启动应用程序

   MyDialog.Run();

//--- 应用程序成功初始化

   return(INIT_SUCCEEDED);

  }

//+------------------------------------------------------------------+

//| EA交易去初始化函数                                                 |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

  {

//--- 销毁对话框

   MyDialog.Destroy(reason);

  }

//+------------------------------------------------------------------+

//| EA交易图表事件函数                                                 |

//+------------------------------------------------------------------+

void OnChartEvent(const int id,         // 事件 ID  

                  const long& lparam,   // long型事件参数

                  const double& dparam, // double型事件参数

                  const string& sparam) // string型事件参数

  {

//--- 调用图表事件父类(这里是CAppDialog)的处理程序

   MyDialog.ChartEvent(id,lparam,dparam,sparam);

  }

启动应用程序的外观和点击结果的按键在截图中提供。

程序的完整源代码

//+------------------------------------------------------------------+

//|                                                Panel_Buttons.mq5 |

//|                        Copyright 2017, MetaQuotes Software Corp. |

//|                                             https://www.mql5.com |

//+------------------------------------------------------------------+



#property copyright "Copyright 2017, MetaQuotes Software Corp."

#property link      "https://www.mql5.com"

#property version   "1.00"

#property description "The panel with several CButton buttons"

#include <Controls\Dialog.mqh>

#include <Controls\Button.mqh>

//+------------------------------------------------------------------+

//| 定义                                                              |

//+------------------------------------------------------------------+

//--- 缩进和间隔 

#define INDENT_LEFT                         (11)      // 从左缩进(预留边框宽度)

#define INDENT_TOP                          (11)      // 从上缩进(预留边框宽度)

#define CONTROLS_GAP_X                      (5)       // X坐标间隔

#define CONTROLS_GAP_Y                      (5)       // Y坐标间隔

//--- 按键

#define BUTTON_WIDTH                        (100)     // X坐标大小

#define BUTTON_HEIGHT                       (20)      // Y坐标大小

//--- 显示区域

#define EDIT_HEIGHT                         (20)      // Y坐标大小



//--- 创建自定义函数类型

typedef int(*TAction)(string,int);

//+------------------------------------------------------------------+

//|  打开文件                                                         |

//+------------------------------------------------------------------+

int Open(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(1);

  }

//+------------------------------------------------------------------+

//|  保存文件                                                         |

//+------------------------------------------------------------------+

int Save(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(2);

  }

//+------------------------------------------------------------------+

//|  关闭文件                                                         |

//+------------------------------------------------------------------+

int Close(string name,int id)

  {

   PrintFormat("%s function called (name=%s id=%d)",__FUNCTION__,name,id);

   return(3);

  }

//+------------------------------------------------------------------+

//| 通过事件处理函数创建按键类                                           |

//+------------------------------------------------------------------+

class MyButton: public CButton

  {

private:

   TAction           m_action;                    // 图表事件处理程序

public:

                     MyButton(void){}

                    ~MyButton(void){}

   //--- 构造函数指定按键文本和事件处理函数指针 

                     MyButton(string text,TAction act)

     {

      Text(text);

      m_action=act;

     }

   //--- 设置OnEvent()事件处理程序调用的自定义函数

   void              SetAction(TAction act){m_action=act;}

   //--- 标准图表事件处理程序

   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) override

     {

      if(m_action!=NULL && lparam==Id())

        {

         //--- 调用自定义处理程序

         m_action(sparam,(int)lparam);

         return(true);

        }

      else

      //--- 返回调用CButton父类处理程序的结果

         return(CButton::OnEvent(id,lparam,dparam,sparam));

     }

  };

//+------------------------------------------------------------------+

//| CControlsDialog 类                                                |

//| 目标:管理应用程序的图形面板                                          |

//+------------------------------------------------------------------+

class CControlsDialog : public CAppDialog

  {

private:

   CArrayObj         m_buttons;                     // 按键数组

public:

                     CControlsDialog(void){};

                    ~CControlsDialog(void){};

   //--- 创建

   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) override;

   //--- 添加按键

   bool              AddButton(MyButton &button){return(m_buttons.Add(GetPointer(button)));m_buttons.Sort();};

protected:

   //--- 创建按键 

   bool              CreateButtons(void);

  };

//+------------------------------------------------------------------+

//| 在图表上创建CControlsDialog 对象                                   |

//+------------------------------------------------------------------+

bool CControlsDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)

  {

   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))

      return(false);

   return(CreateButtons());

//---

  }

//+------------------------------------------------------------------+

//| 创建和添加按键到CControlsDialog面板                                 |

//+------------------------------------------------------------------+

bool CControlsDialog::CreateButtons(void)

  {

//--- 计算按键坐标

   int x1=INDENT_LEFT;

   int y1=INDENT_TOP+(EDIT_HEIGHT+CONTROLS_GAP_Y);

   int x2;

   int y2=y1+BUTTON_HEIGHT;

//--- 添加带有函数指针的按键对象

   AddButton(new MyButton("Open",Open));

   AddButton(new MyButton("Save",Save));

   AddButton(new MyButton("Close",Close));

//--- 创建图形按键

   for(int i=0;i<m_buttons.Total();i++)

     {

      MyButton *b=(MyButton*)m_buttons.At(i);

      x1=INDENT_LEFT+i*(BUTTON_WIDTH+CONTROLS_GAP_X);

      x2=x1+BUTTON_WIDTH;

      if(!b.Create(m_chart_id,m_name+"bt"+b.Text(),m_subwin,x1,y1,x2,y2))

        {

         PrintFormat("Failed to create button %s %d",b.Text(),i);

         return(false);

        }

      //--- 添加每个按键到CControlsDialog 集合

      if(!Add(b))

         return(false);

     }

//--- 成功

   return(true);

  }

//--- 声明全局对象以便在启动程序时自动创建

CControlsDialog MyDialog;

//+------------------------------------------------------------------+

//| EA交易初始化函数                                                   |

//+------------------------------------------------------------------+

int OnInit()

  {

//--- 现在,在图表上创建对象

   if(!MyDialog.Create(0,"Controls",0,40,40,380,344))

      return(INIT_FAILED);

//--- 启动应用程序

   MyDialog.Run();

//--- 应用程序成功初始化

   return(INIT_SUCCEEDED);

  }

//+------------------------------------------------------------------+

//| EA交易去初始化函数                                                 |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

  {

//--- 销毁对话框

   MyDialog.Destroy(reason);

  }

//+------------------------------------------------------------------+

//| EA交易图表事件函数                                                 |

//+------------------------------------------------------------------+

void OnChartEvent(const int id,         // 事件 ID  

                  const long& lparam,   // long型事件参数

                  const double& dparam, // double型事件参数

                  const string& sparam) // string型事件参数

  {

//--- 调用图表事件父类(这里是CAppDialog)的处理程序

   MyDialog.ChartEvent(id,lparam,dparam,sparam);

  }

另见

变量函数