在MQL4编程中,我们可以通过使用typedef关键字来创建自定义的数据类型。这不是创建一个新的数据类型,而是为已存在的数据类型定义一个新的名字。自定义类型可以使我们的应用更加灵活,只需更改typedef指令,即可使用替代宏(#define)。同时,自定义类型也可以提高代码的可读性,因为我们可以使用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 (*FuncType)(int,int);
FuncType func_ptr; // 函数指针
//--- 声明符合FuncType描述的函数
int subtract(int x,int y) { return(x-y); } // 从一个数中减去另一个数
int plus(int x,int y) { return(x+y); } // 两个数相加
int invert(int x) { return(~x); } // 变量中的位反转
//--- func_ptr变量可以存储函数地址以便后续声明
func_ptr=subtract;
Print(func_ptr(10,5));
func_ptr=plus;
Print(func_ptr(10,5));
func_ptr=invert; // 错误: invert没有int (int,int) 类型
Print(func_ptr(10)); // 错误: 需要两个参数
在这个例子中,func_ptr变量可以接收subtract和plus函数,因为它们都有两个int类型的输入,如FuncType函数指针所定义的。相反,invert函数不能被分配给func_ptr指针,因为它的签名是不同的。
用户界面中事件模型的排列
函数指针使您能够在创建用户界面时轻松创建事件处理。首先,定义一个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函数指针。接下来,从CAppDialog创建CControlsDialog派生类,向其中添加m_buttons数组以存储MyButton类型的按钮,以及AddButton(MyButton &button)和CreateButtons()方法。
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));
}
};
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);
};
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 …[省略]
以上就是在MQL4编程中如何使用自定义类型和函数指针来提高代码的灵活性和可读性。