一、事件处理函数
1、OnInit / OnDeinit / OnTick
OnInit:当指标或EA第一次加载时,执行该函数。
OnDeinit:卸载指标或EA,或者关闭加载该指标或EA的图表时,执行该函数。
OnTick:在价格每波动一次,执行一次该函数。
int OnInit()
{
printf("init");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
printf("tick");
}
2、OnTimer
计时器设定的固定时间间隔,执行该函数。
int OnInit()
{
EventSetTimer(3); // 设定定时器,每隔3s,执行一次OnTimer函数
printf("init");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
if(TimeLocal()>= D'2025.2.6 17:13:00')
{
EventKillTimer(); // 清除定时器
}
}
void OnTimer()
{
printf(TimeToString(TimeLocal(), TIME_DATE|TIME_SECONDS));
}
3、OnTrade / OnTradeTransaction
OnTrade:对即时交易或者挂单进行开仓、平仓、修改订单,都执行该函数。由于该函数难以把控,一般不使用。
OnTradeTransaction:事件触发条件同 OnTrade
int OnInit()
{
printf("init");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
}
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
if(trans.type==TRADE_TRANSACTION_REQUEST)
// trans.type == TRADE_TRANSACTION_REQUEST: 检查交易类型是否为交易请求。
{
if(request.action==TRADE_ACTION_DEAL && request.type==ORDER_TYPE_BUY)
// TRADE_ACTION_DEAL 即时交易
// ORDER_TYPE_BUY 订单类型是买单 注:空单平仓视为开多单
{
Alert("你已经开多单");
}
}
}
4、OnChartEvent
OnChartEvent:当在图表上进行一些操作时,触发事件,执行该回调函数。
int OnInit()
{
printf("init");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
}
void OnChartEvent(
const int id,
const long& lparam,
const double& dparam,
const string& sparam)
{
if(id==CHARTEVENT_CLICK)
{
Alert("你单击了鼠标"+"x:"+lparam+"y:"+dparam);
}
}
5、OnTester
OnTester:在EA交易测试历史数据完毕后,执行该函数。
int OnInit()
{
printf("init");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
printf("hello");
}
double OnTester()
{
printf("执行完了!");
return(11.0);
}
6、OnTesterInit / OnTesterPass / OnTesterDeinit
EA优化时,触发这些函数。
详细说明,暂略。
7、OnBookEvent
OnBookEvent:在市场深度变化时触发。
int OnInit()
{
printf("init");
MarketBookAdd(Symbol());
// MarketBookAdd() 将一个交易品种的市场深度数据添加到系统中
// Symbol() 返回当前图表的交易品种名称。
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
printf("hello");
}
void OnBookEvent(const string& symbol)
{
printf(symbol+"这个货币市场深度发生了变化");
}
8、OnCalculate
OnCalculate:指标专有的事件处理函数,每个新的 tick 到来,触发一次该函数。
int OnInit()
{
return(INIT_SUCCEEDED);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
printf(open[1]);
return(rates_total);
}
9、OnStart
OnStart:脚本专有的事件处理函数,加载脚本后,只执行一次。
int OnStart()
{
Alert("脚本执行完了!");
return(1);
}
二、初级语法
1、常用基本数据类型
整型 int
浮点型 double
字符串 string
颜色 color
时间 datetime
int OnStart()
{
int a = 123;
double b = 1.23;
string c = "abc";
color d = clrDarkViolet;
// 颜色也可以定义为C'R,G,B',取值范围为0-255
datetime e = D'2025.02.16 05:05';
return(1);
}
2、常量
(1)标准常量
系统自带的的常量。
int OnStart()
{
int x = PERIOD_M15; // PERIOD_M15为标准常量
return(1);
}
(2)自定义常量
#define changliang "abc"
int OnStart()
{
string s = changliang;
return(1);
}
3、变量
(1)预定义变量
每个预定义变量都对应一个函数,以后编程时,可以直接使用该函数,来代替预定义变量。
int OnStart()
{
int f = _Digits; // 预定义变量
int g = Digits();
return(1);
}
(2)自定义变量
自己定义的变量,如上述的 int a = 123;,a就为自定义变量。
4、函数
(1)系统自带的函数
int OnStart()
{
Alert("报警");
return(1);
}
(2)自定义函数
int OnStart()
{
int a = 12;
int b = 2;
int s = add(a,b);
return(1);
}
int add(int x, int y)
{
return(x+y);
}
(3)函数转换
函数转化函数用来进行数据类型转换。
int OnStart()
{
int a = 12;
string shijian = "2025.02.16 21.44";
string k = IntegerToString(a);
datetime j = StringToTime(shijian);
string g = StringFormat("姓名:%s 年龄:%d 身高:%.2f","LMX",18,170.555);
printf("姓名:%s 年龄:%d 身高:%.2f","LMX",18,170.555); // 输出内容同g
return(1);
}
(4)字符串函数
字符串函数用来处理字符串
int OnStart()
{
string a1 = "asgqwer0";
int ret1 = StringFind(a1,"gq",0); // 返回匹配字符串的位置索引
string b1 = StringSubstr(a1,2,3); // gqw
string c1 = "张三,18,170.5";
string c2[];
StringSplit(c1, ',', c2);
return(1);
}
5、变量和函数的作用域
(1)外部参数变量
外部参数变量也是全局变量,它的值通常在运行时通过用户界面(如弹出框)进行设置。
#property script_show_inputs // 在脚本运行时显示输入参数窗口
input double lots=0.1; // 外部参数变量
int OnStart()
{
Alert(lots);
return(1);
}
(2)全局变量
全局变量可以被所有函数使用。
int magic = 456; // 全局变量
int OnStart()
{
printf("%d",magic);
a();
return(1);
}
void a()
{
printf("%d",magic+1);
}
(3)局部变量
函数内部定义的变量,包括形参。局部变量只能在函数内部使用。
(4)函数引用参数变量
引用参数变量相当于给被引用变量起了一个别名,实际上引用参数变量和被引用变量指向同一块内存空间。
int OnStart()
{
int a = 11;
int b = 22;
mul(a, b);
printf("a=%d,b=%d",a,b); // a=110,b=22
return(1);
}
void mul(int &x, int y)
{
x = x*10;
y = y*10;
}
三、中级语法
1、程序端全局变量
程序端全局变量和我们自定义的普通全局变量是不一样的,它存储在平台的内存中,并且可以跨不同的脚本或EA共享。它需要用GlobalVariableSet()创建。
// 防止多次开单示意程序
// 即使退出MT5程序,程序端全局变量name + "duook"的值也会保存
string name;
int magic = 123;
int OnInit()
{
name = MQLInfoString(MQL_PROGRAM_NAME) + Symbol() + IntegerToString(magic);
// MQLInfoString(MQL_PROGRAM_NAME) 返回已启动的MQL5程序的名称
if(GlobalVariableCheck(name + "duook") == false) // 检查程序端全局变量是否存在
{
GlobalVariableSet(name + "duook", 0); // 将程序端全局变量name + "duook"的值设置为0
}
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
if(GlobalVariableGet(name + "duook") == 0)
{
// 在这里可以写开多单的代码
GlobalVariableSet(name + "duook", 1);
}
}
2、枚举类型
// 自定义枚举类型duokong
// 将duo设为1,默认为0。kong默认为上一个枚举常量加+1,以此类推。
// 枚举常量的注释会在加载EA弹出的窗口中显示出来
enum duokong
{
duo = 1, // 只开多
kong, // 只开空
duokong // 多空都可以开
};
// 定义枚举类型为duokong的变量openDirection,默认值设为duo
input duokong openDirection=duo;
int OnInit()
{
Print(openDirection);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
}
也可以使用系统预定义的枚举类型
input ENUM_TIMEFRAMES period=PERIOD_H1;
int OnInit()
{
Print(period);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
}
3、结构体类型
结构体成员可以是不同数据类型。
// 定义结构体类型kbar
struct kbar
{
double open;
double close;
double high;
double low;
datetime time;
int vol;
};
int OnInit()
{
kbar kb = {1.1,2.1,3.1,0.1,TimeCurrent(),55}; // 声明结构体变量kb,并且初始化
kb.vol = 60; // 修改结构体变量kb的成员vol的值。也可以直接用该方法对成员进行初始化。
MqlRates mr; // 使用系统预定义的结构体类型MqlRates,去定义结构体变量mr
mr.open= 1.1; // 修改结构体变量成员open的值
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
printf("deinit");
}
void OnTick()
{
}
4、数组
double a[10]; //定义double类型静态数组,元素从a[0]到a[9]
int OnInit()
{
double c[]; // 声明一个 double 类型的动态数组 c
ArrayResize(c, 2); // 调整数组 c 的大小为 2
return(INIT_SUCCEEDED);
}
int OnInit()
{
MqlRates rates[]; // 声明一个 MqlRates 类型的动态数组,用来存储历史数据
ArraySetAsSeries(rates, true); // 将数组设为倒序排列,最新的历史数据存放在数组的索引 0 位置
CopyRates(NULL,0,0,100,rates); // 获取 100 根 K 线数据,复制到数组rates中
// NULL:表示当前图表的符号
// 0:表示当前的时间周期
// 0:表示从当前时间开始获取数据
// 100:表示获取 100 根历史K线数据。
// rates:存储获取到的数据的数组。
double op = rates[0].open; // 获取第 0 根 K 线的开盘价,即当前K线开盘价
return(INIT_SUCCEEDED);
}
数组函数:
int OnInit()
{
int aok[5];
ArrayInitialize(aok,1); // 将数组aok的元素都初始化为1
ArrayFill(aok,2,2,3); // 将数组aok的索引为2开始,两个元素的值设置为3
return(INIT_SUCCEEDED);
}
5、循环判断语句
int OnInit()
{
int a = 1;
if(a > 1)
{
Print("大于");
}
else if(a == 1)
{
Print("等于");
}
else
{
Print("小于");
}
return(INIT_SUCCEEDED);
}
int OnInit()
{
int w = 0;
while(w<5)
{
Print(w);
w++;
}
return(INIT_SUCCEEDED);
}
int OnInit()
{
for(int i=0; i<10; i++)
{
Print(i);
}
return(INIT_SUCCEEDED);
}
6、算术、关系、逻辑运算
比较简单,参考手册 MQL5参考 / 语言基础 / 运行式和表达式
其中,&& 为且,|| 为或。
int OnInit()
{
bool a = true;
bool b = false;
if(a || b)
{
Print("对");
}
else
{
Print("错");
}
return(INIT_SUCCEEDED);
}
四、高级语法
1、类的创建
类一般放在 Include 文件夹下,并且一个类写在一个头文件中。
创建类可以在 Include 文件夹中,创建一个子文件夹 myClass,然后在 myClass 中创建一个类文件 testClass。
myClass\testClass.mqh 代码如下:
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
printf(__FUNCTION__); // 预定义宏,返回函数名称
return(x+y);
}
A() // 构造函数,实例对象创建时,被调用
{
printf(__FUNCTION__);
}
~A() // 析构函数,用于类的实例销毁时调用
{
printf(__FUNCTION__);
}
};
2、对象的定义
在栈上创建类的对象(推荐):
#include <myClass\testClass.mqh>
// 引入自定义类的头文件testClass.mqh
// 引入文件的根目录是Include
int OnStart()
{
A ac;
return(1);
}
对象 ac 的生命周期与当前作用域相关,当 OnStart() 函数执行完毕时,ac 对象会被销毁。
在堆上创建类的对象(动态定义):
#include <myClass\testClass.mqh>
int OnStart()
{
A *ac2 = new A();
// new A() 会返回一个指向 A 类型对象的指针,这个指针被赋值给 ac2。
delete ac2;
// 由于 ac2 是指针类型,需要使用 delete 来手动释放堆上分配的内存,避免内存泄漏。
// 不手动释放内存,即使脚本结束,也不会释放掉
return(1);
}
3、带参数的类和对象的定义
带一个参数:
类:
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
printf(__FUNCTION__); // 预定义宏,返回函数名称
return(x+y);
}
A(int x)
{
this.b = x;
// this 是一个指针,它指向当前类的实例对象。
// 在类的成员函数中,this 用来引用当前对象。
// this 只能在成员函数中使用,用来指代当前正在调用该函数的对象。
printf(__FUNCTION__);
}
~A() // 析构函数,用于类的实例销毁时调用
{
printf(__FUNCTION__);
}
};
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
// A ac(2);
A ac = 10; // 效果同上
Print(ac.b);
return(1);
}
带多个参数:
类:
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
printf(__FUNCTION__); // 预定义宏,返回函数名称
return(x+y);
}
A(int x, double y, string z)
{
this.b = x;
this.c = y;
this.d = z;
printf(__FUNCTION__);
}
~A() // 析构函数,用于类的实例销毁时调用
{
printf(__FUNCTION__);
}
};
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
A ac(10, 0.1, "ok");
Print(ac.b, ac.c, ac.d);
return(1);
}
4、类的封装
在 MQL5 中,public、private 和 protected 是 访问控制符,它们用于控制类的成员的访问权限。
public:成员可以被类的外部代码、类的内部代码以及派生类访问。
private:成员只能在类内部的成员函数中访问,外部代码或派生类不能直接访问。
protected:成员既可以在当前类的成员函数中访问,也可以在派生类的成员函数中访问,但不能在外部直接访问。
类:
class A
{
private:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
printf(__FUNCTION__); // 预定义宏,返回函数名称
return(x+y);
}
public:
A(int x, double y, string z)
{
this.b = x;
this.c = y;
this.d = z;
printf(__FUNCTION__);
}
~A() // 析构函数,用于类的实例销毁时调用
{
printf(__FUNCTION__);
}
};
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
A ac(10, 0.1, "ok");
Print(ac.b, ac.c, ac.d); // 会报错
return(1);
}
5、类的函数重载
根据传入实参函数种类及个数的不同,选择不同的成员函数。
类:
class A
{
private:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
return(x+y);
}
public:
A(int x, double y, string z)
{
b = x;
this.c = y;
this.d = z;
Print(this.b);
}
A() // 函数重载
{
}
~A() // 析构函数,用于类的实例销毁时调用
{
}
};
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
A ac(10, 0.1, "ok");
A ac2; // 实例化时,会调用第二个构造函数
A ac3[4]; // 无参数的构造函数,对应的对象可以组成数组
return(1);
}
6、类的成员函数提前声明
类的成员函数的声明和定义可以不同时。
类:
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y);
A() // 构造函数
{
}
~A() // 析构函数,用于类的实例销毁时调用
{
}
};
int A::add(int x, int y)
{
return(x+y);
}
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
A ac;
int x=ac.add(3,20);
Print(x);
return(1);
}
7、const类型变量和函数
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(const int x, int y)
{
x=5; // 报错,因为const修饰的是常变量,在函数内不能修改
return(x+y);
}
A() // 构造函数
{
}
~A() // 析构函数,用于类的实例销毁时调用
{
}
};
class A
{
public:
// 声明成员变量
int b;
double c;
string d;
// 声明成员函数
int add(int x, int y) const
{
this.b = 5; // 报错,因为const类型函数不能修改成员变量。
return(x+y);
}
A() // 构造函数
{
}
~A() // 析构函数,用于类的实例销毁时调用
{
}
};
8、类的静态成员函数
#include <myClass\testClass.mqh>
int OnStart()
{
A ac;
int x = ac.add(3,20);
int y = A::add(4,5); // 静态成员函数可以不创建对象,直接访问
// :: 是用于访问类成员的运算符。
Print(x);
Print(y);
return(1);
}
9、类的静态成员变量
静态成员变量属于类本身,不属于某个实例(对象)。所有对象共享一个静态变量。
静态成员变量的初始化必须在类外部进行。
类:
class A
{
public:
// 声明成员变量
static int b; // 声明静态成员变量
double c;
string d;
// 声明成员函数
int add(int x, int y)
{
return(x+y);
}
A() // 构造函数
{
}
~A() // 析构函数,用于类的实例销毁时调用
{
}
};
int A::b = 10; // 初始化静态成员变量
对象:
#include <myClass\testClass.mqh>
int OnStart()
{
Print(A::b);
return(1);
}
10、类的继承
子类继承父类后,会继承它的成员函数和成员变量(访问权限允许的成员)。
(1)公有继承
ParentClass.mqh:
class Parent
{
private:
void parentPrivate()
{
printf("parentPrivate!");
}
protected:
int parProtected;
void parentProtected()
{
printf("parentProtected!");
}
public:
void parentPublic()
{
printf("parentPublic!");
}
Parent() // 构造函数
{
}
~Parent() // 析构函数
{
}
};
SonClass.mqh:
#include <myclass/ParentClass.mqh>
class Son:public Parent // 公有继承
{
public:
void call_parentPublic()
{
parentPublic();
}
void call_parentProtected()
{
parentProtected();
}
/* 报错
void call_parentPrivate()
{
parentPrivate();
}
*/
void func()
{
Parent::parProtected=10; // 子类可以调用父类的保护成员变量
Print(parProtected);
}
};
脚本:
#include <myClass\sonClass.mqh>
int OnStart()
{
Son obj;
obj.call_parentPublic();
obj.parentPublic(); // 可以直接调用父类的成员
obj.call_parentProtected();
// obj.parentProtected(); // 报错,子类继承父类的保护成员,还是保护成员,所以对象不能调用。
return(1);
}
(2)私有继承
ParentClass.mqh:
class Parent
{
private:
void parentPrivate()
{
printf("parentPrivate!");
}
protected:
void parentProtected()
{
printf("parentProtected!");
}
public:
void parentPublic()
{
printf("parentPublic!");
}
Parent() // 构造函数
{
}
~Parent() // 析构函数
{
}
};
SonClass.mqh:
#include <myclass/ParentClass.mqh>
class Son:private Parent // 私有继承
{
public:
void call_parentPublic()
{
parentPublic();
}
void call_parentProtected()
{
parentProtected();
}
/* //报错
void call_parentPrivate()
{
parentPrivate();
}
*/
};
脚本:
#include <myClass\sonClass.mqh>
int OnStart()
{
Son obj;
obj.call_parentPublic();
// obj.parentPublic(); // 报错,子类继承父类的公有成员是私有成员,对象不能直接访问
obj.call_parentProtected();
// obj.parentProtected(); // 报错,子类继承父类的保护成员是私有成员,对象不能直接访问
return(1);
}
(3)保护继承
ParentClass.mqh:
class Parent
{
private:
void parentPrivate()
{
printf("parentPrivate!");
}
protected:
void parentProtected()
{
printf("parentProtected!");
}
public:
void parentPublic()
{
printf("parentPublic!");
}
Parent() // 构造函数
{
}
~Parent() // 析构函数
{
}
};
SonClass.mqh:
#include <myclass/ParentClass.mqh>
class Son:protected Parent // 保护继承
{
public:
void call_parentPublic()
{
parentPublic();
}
void call_parentProtected()
{
parentProtected();
}
/* //报错
void call_parentPrivate()
{
parentPrivate();
}
*/
};
脚本:
#include <myClass\sonClass.mqh>
int OnStart()
{
Son obj;
obj.call_parentPublic();
// obj.parentPublic(); // 报错,子类继承父类的公有成员是保护成员,对象不能直接访问
obj.call_parentProtected();
// obj.parentProtected(); // 报错,子类继承父类的保护成员是保护成员,对象不能直接访问
return(1);
}
(4)三种继承方式总结
子类的继承方式对父类成员访问权限的影响:
继承方式 | 父类 公有成员 | 父类 保护成员 | 父类 私有成员 |
---|---|---|---|
公有继承 | 公有 | 保护 | 无法访问 |
私有继承 | 私有 | 私有 | 无法访问 |
保护继承 | 保护 | 保护 | 无法访问 |
(5)子类创建与销毁对象的执行顺序
创建实例的执行顺序为:
1、执行到类Son构造函数开始(并未执行其函数内部代码)。
2、执行类Parent的构造函数。
3、执行类Son构造函数内部代码。
销毁实例的执行顺序为:
1、执行类Son析构函数内部代码。
2、执行类Parent析构函数。
3、返回到类Son析构函数结尾。
ParentClass.mqh:
class Parent
{
public:
Parent() // 构造函数
{
printf(__FUNCTION__);
}
~Parent() // 析构函数
{
printf(__FUNCTION__);
}
};
sonClass.mqh:
#include <myclass/ParentClass.mqh>
class Son:public Parent
{
public:
Son() // 构造函数
{
printf(__FUNCTION__);
}
~Son() // 析构函数
{
printf(__FUNCTION__);
}
};
脚本:
#include <myClass\sonClass.mqh>
int OnStart()
{
Son obj;
return(1);
}
(6)成员初始化列表
ParentClass.mqh
class Parent
{
public:
Parent(int a) // 构造函数
{
Print(a);
}
~Parent() // 析构函数
{
}
};
sonClass.mqh
#include <myclass/ParentClass.mqh>
class Son:public Parent // 公有继承
{
public:
// 语法 构造函数:成员初始化列表
// 成员初始化列表会在子类构造函数的函数体执行之前,先执行
// 成员初始化列表如果不显示调用父类构造函数,会默认执行Parent()
Son(int b):Parent(b)
{
}
~Son()
{
}
};
脚本
#include <myClass\sonClass.mqh>
int OnStart()
{
Son obj(5);
return(1);
}
(7)函数的重载与重写
函数重载: 指的是在同一个类或父子类中 定义多个函数,它们具有相同的函数名,但参数列表不同(参数个数、类型或顺序不同)。函数的返回类型不影响重载,因为重载是基于参数列表来区分的。
函数重写: 是指 子类重新定义 从父类继承来的函数,以提供特定的实现。重写的函数必须具有 相同的函数签名(包括名称、参数类型、返回类型)。
注:无论是函数的重载还是重写,都优先找子类。在子类的成员函数能找到相应的函数,即函数名和参数列表都能对上,就不再去找父类。如果对不上,才去找父类。
11、类的多态与虚函数、纯虚函数、抽象类
ParentClass.mqh
class Parent
{
public:
// 关键字 virtual 会使函数编程虚函数。
// 如果不加关键字 virtual,脚本执行obj.func();语句,会执行父类的方法,而不执行子类的方法。
// 如果加关键字 virtual,脚本执行obj.func();语句,会执行子类的方法,而不执行父类的方法。
virtual void func()
{
printf(__FUNCTION__);
}
Parent() // 构造函数
{
}
~Parent() // 析构函数
{
}
};
sonClass.mqh
#include <myclass/ParentClass.mqh>
class Son:public Parent // 公有继承
{
public:
// override 修饰符表示声明的函数必须为重写父类的虚函数,否则会报错。
// override可以不写,直接写重写的函数,不过建议写。
// 如果不重写func()函数,那么脚本执行obj.func();时,会调用父类的func()函数。
void func() override
{
printf(__FUNCTION__);
}
Son()
{
}
~Son()
{
}
};
脚本
#include <myClass\sonClass.mqh>
int OnStart()
{
Parent *obj = new Son(); // 多态
obj.func();
return(1);
}
抽象类是包含至少一个纯虚函数的类。
抽象类不能直接实例化。
抽象类的子类如果不重写抽象类,子类实例化时会报错。
class Parent // 抽象类
{
public:
virtual void func()=0; // 定义纯虚函数
Parent() // 构造函数
{
}
~Parent() // 析构函数
{
}
};