MQL5-构造函数和析构函数
构造函数是一种特殊函数,当创建结构或类的对象时自动调用并且通常用来初始化类成员。 从更长远来看我们将只看重类,并且同样应用于架构中,除非是另行定义。构造函数的名称要与类函数的名称匹配。构造函数不需要返回类型(您可以指定空型 )。
定义类成员 – 字符串 动态数组和需要初始化的对象 – 无论如何都需要初始化,不管构造函数是否存在。
每个类可以有多个构造函数,根据参数数量和初始化列表而有所不同。需要指定参数的构造函数称为参数构造函数。
无参数构造函数被称为缺省构造函数。如果一个类中没有声明构造函数,编译器会在编译过 程中创建一个缺省构造函数。
//+------------------------------------------------------------------+
//| 处理日期的类 |
//+------------------------------------------------------------------+
class MyDateClass
{
private:
int m_year; // 年
int m_month; // 月
int m_day; // 几月几日
int m_hour; // 某天几时
int m_minute; // 分钟
int m_second; // 秒
public: //--- 缺省构造函数
MyDateClass(void); //--- 参数构造函数
MyDateClass(int h,int m,int s);
};
构造函数可以在类描述中声明,然后定义主体。例如,MyDateClass的两个构造函数可以定义如下:
//+------------------------------------------------------------------+
//| 默认构造函数 |
//+------------------------------------------------------------------+
MyDateClass::MyDateClass(void)
{
//--
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year=mdt.year;
m_month=mdt.mon;
m_day=mdt.day;
m_hour=mdt.hour;
m_minute=mdt.min;
m_second=mdt.sec;
Print(__FUNCTION__);
}
//+------------------------------------------------------------------+
//| 参数构造函数 |
//+------------------------------------------------------------------+
MyDateClass::MyDateClass(int h,int m,int s)
{
MqlDateTime mdt;
datetime t=TimeCurrent(mdt);
m_year=mdt.year;
m_month=mdt.mon;
m_day=mdt.day;
m_hour=h;
m_minute=m;
m_second=s;
Print(__FUNCTION__);
}
在 默认构造函数,类的所有成员都使用TimeCurrent() 函数,在参数构造函数,只有小时值在使用。其他的类成员 (m_year, m_month 和 m_day) 将自动初始化当前日期。
当初始化类的对象数组时,默认构造函数有一个特殊用途。所有参数都有默认值的构造函数, 并不是默认构造函数。示例如下:
//+------------------------------------------------------------------+
//| 默认构造函数的类 |
//+------------------------------------------------------------------+
class CFoo
{
datetime m_call_time; // 最近一次对象调用时间
public: //--- 带有默认值参数的构造函数不是默认构造函数
CFoo(const datetime t=0){m_call_time=t;};
//--- 复制构造函数
CFoo(const CFoo &foo){m_call_time=foo.m_call_time;};
string ToString(){return(TimeToString(m_call_time,TIME_DATE|TIME_SECONDS));};
};
//+------------------------------------------------------------------+
//| 脚本程序开始函数 |
//+------------------------------------------------------------------+
void OnStart()
{
// CFoo foo; // 该变量不能使用 - 没有设置默认构造函数
//--- 创建 CFoo 对象的可能选项
CFoo foo1(TimeCurrent()); // 参数构造函数的显式调用
CFoo foo2(); // 带有默认参数的参数构造函数的显式调用
CFoo foo3=D'2009.09.09'; // 参数构造函数的隐式调用
CFoo foo40(foo1); // 复制构造函数的显式调用
CFoo foo41=foo1; // 复制构造函数的隐式调用
CFoo foo5; // 默认构造函数的显式调用(如果没有默认构造函数,那么带有默认值的参数构造函数被调用)
//--- 接收 CFoo 指针的可能选项
CFoo *pfoo6=new CFoo(); // 动态创建对象和接收其指针
CFoo *pfoo7=new CFoo(TimeCurrent());// 另一个动态对象创建的选项
CFoo *pfoo8=GetPointer(foo1); // 现在 pfoo8 指向对象 foo1
CFoo *pfoo9=pfoo7; // pfoo9 和 pfoo7 指向一个和相同的对象
// CFoo foo_array[3]; // 该选项不能使用 - 没有指定默认构造函数
//--- 显示m_call_time值
Print("foo1.m_call_time=",foo1.ToString());
Print("foo2.m_call_time=",foo2.ToString());
Print("foo3.m_call_time=",foo3.ToString());
Print("foo4.m_call_time=",foo4.ToString());
Print("foo5.m_call_time=",foo5.ToString());
Print("pfoo6.m_call_time=",pfoo6.ToString());
Print("pfoo7.m_call_time=",pfoo7.ToString());
Print("pfoo8.m_call_time=",pfoo8.ToString());
Print("pfoo9.m_call_time=",pfoo9.ToString());
//--- 删除动态创建数组
delete pfoo6;
delete pfoo7;
//删除 pfoo8; // 您不需要一定删除pfoo8,因为它指向自动创建的对象foo1
//删除 pfoo9; // 您不需要一定删除pfoo9,因为它指向pfoo7相同的对象
}
如果您取消这些字符串
//CFoo foo_array[3]; // 该变量不能使用 - 没有设置默认构造函数
或
//CFoo foo_dyn_array[]; // 该变量不能使用 - 没有设置默认构造函数
然后编译器将会返回一个错误“ 默认构造函数未定义” 。
如果类有用户定义构造函数,编译器就不会生成默认构造函数。这意味着如果一个类中声明参数构造函数,但未声明默认构造函数,则您不能声明类对象的数组。编译器将返回这个脚本错误:
//+------------------------------------------------------------------+
//| 无默认构造函数的类 |
//+------------------------------------------------------------------+
class CFoo
{
string m_name;
public: CFoo(string name) { m_name=name;}
};
//+------------------------------------------------------------------+
//| 脚本程序开始函数 |
//+------------------------------------------------------------------+
void OnStart()
{
//--- 编译过程中收到“默认构造函数未定义”的错误
CFoo badFoo[5];
}
在该示例中,CFoo 类拥有声明的参数构造函数在这种情况下,编译器在编译过程中不能自动创建默认构造函数。同时当您声明对象数组时,假设所有对象应该自动创建和初始化。 对象自动初始化过程中,需要调用默认构造函数,但由于默认构造函数不是显式声明并且不会通过 编译器自动生成,所以不可能创建这种对象。因此,编译器在编译阶段会生成一个错误。
有一个使用构造函数初始化对象的特殊语法。结构和类成员的构造函数初始化软件(特殊结构初始化)可以在初始化列表中指定。
初始化列表就是通过逗号隔开的初始化软件列表,它在构造函数参数列表后主体前(左大括号前)的冒号后面。它有以下几点要求:
- 初始化列表仅能用于构造函数
- 父成员不能在初始化列表中初始化
- 初始化列表必须遵循一个函数 定义 (实施)
这是一个用于初始化类成员的几个构造函数的示例。
//+------------------------------------------------------------------+
//| 存储字符名称的类 |
//+------------------------------------------------------------------+
class CPerson
{
string m_first_name; // 第一名称
string m_second_name; // 第二名称
public:
//--- 空默认构造函数
CPerson() {Print(__FUNCTION__);};
//--- 参数构造函数
CPerson(string full_name);
//--- 初始化列表的构造函数
CPerson(string surname,string name): m_second_name(surname), m_first_name(name) {};
void PrintName(){PrintFormat("Name=%s Surname=%s",m_first_name,m_second_name);};
}; //+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CPerson::CPerson(string full_name)
{
int pos=StringFind(full_name," ");
if(pos>=0)
{
m_first_name=StringSubstr(full_name,0,pos);
m_second_name=StringSubstr(full_name,pos+1);
}
}
//+------------------------------------------------------------------+
//|脚本程序开始函数 |
//+------------------------------------------------------------------+
void OnStart()
{
//--- 收到“默认构造函数未定义”的错误
CPerson people[5];
CPerson Tom="Tom Sawyer"; // 汤姆 索亚
CPerson Huck("Huckleberry","Finn"); // 哈克贝利 费恩
CPerson *Pooh = new CPerson("Winnie","Pooh"); // 维尼熊
//--- 输出值
Tom.PrintName();
Huck.PrintName();
Pooh.PrintName();
//--- 删除一个动态创建的对象
delete Pooh;
}
在这种情况下,CPerson 类有三种构造函数:
- 显式默认构造函数, 允许创建该类对象的数组;
- 单参数构造函数,会得到一个作为参数的完整名称并根据发现的空间分成名称和第二名称;
- 双参数的构造函数包含初始化列表。初始化软件 m_second_name(姓)和 m_first_name (名)。
注意使用列表初始化已经替代了一个任务。个体成员必须初始化为:
class_member (表达式列表)
在初始化列表,成员可按任意顺序排列,但所有的类成员将会根据它们公告的顺序初始化。这意味着在第三构造函数,第一个m_first_name成员将会初始化,因为它是最先公告的,并且仅在 m_second_name初始化之后。如果一些类成员的初始化取决于其他类成员的值,则应该将此考虑其中。
如果默认构造函数没有在基本类声明,而同时声明一个或多个参数函数,您应该保持调用初始化列表中的一个基本类的构造函数。它作为列表普通成员用逗号分隔并且无论初始化列表位于哪里,都在对象初始化时被最先调用。
//+------------------------------------------------------------------+
//| 基本类 |
//+------------------------------------------------------------------+
class CFoo
{
string m_name;
public:
//--- 初始化列表的构造函数
CFoo(string name) : m_name(name) { Print(m_name);} };
//+------------------------------------------------------------------+
//| 派生自CFoo 的类 |
//+------------------------------------------------------------------+
class CBar : CFoo
{
CFoo m_member; // 类成员是父对象
public:
//--- 初始化列表中的默认构造函数调用父构造函数
CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);} };
//+------------------------------------------------------------------+
//| 脚本程序开始函数 |
//+------------------------------------------------------------------+
void OnStart()
{
CBar bar;
}
例如,创建柱对象时,将会调用默认构造函数CBar(),这里首先调用父CFoo构造函数,然后是 m_member类成员的构造函数。
析构函数是一种特殊功能,当类目标被破坏时自动调用,析构函数的名称用波浪字符(~)以分类 名输入。串型数据、动态数组和目标函数,不管破坏函数是否出现,无论如何都不会初始化。如 果存在破坏函数,该行为在召回破坏者后会执行。
破坏函数总是虚拟的 ,无论虚拟值存在与否。