MQL5语言帮助文件人话版(二十一)数据类型之new和堆

| 发表于 1 小时前 | 显示全部楼层 |复制链接
第二十一章 new和堆
上文说过,原手册中“目标指针”一章,其实讲的是“new”操作符,先介绍了一些前置概念,这一章再开始聊“new”。
麻爪本人是学工程管理的建造师,就以盖房子给大家举个例子。程序所展现出来的效果,也就是你看到的窗口上各种按钮、图表,以及EA的自动下单效果等等,这些都是EA软件的成品,可以看作是盖好的房子,我们编好的那个代码文件其实只是给人类看的程序图纸,并不属于成品。你的电脑上绝对不只运行EA这一个程序,正在运行的各种软件就像一个一个的施工队,内存条就是它们的施工场地,windows就像是工地管理员,发现一个新的施工队进来,就在内存条上给它安排一块地方让它折腾。
当我们的EA程序开始运行后,系统也会给它分配内存,相当于它拿到了一块施工场地,它会大致划分出四个区域:

一、代码段(.text 段)
我们写的EA程序看起来是一条一条的代码,经过编译后变成机器能认识的机器码,对应着你写的所有函数、OnTick 逻辑、循环判断等等的代码实体,这些代码相当于EA盖房子的图纸、施工标准、工艺流程等等这种规范类的东西,EA一打开,先吧这些东西写如代码段,直到EA关闭的时候,才会删除掉。这块区域我们就相当于工地上的“图纸档案室“。

二、全局静态存储区
和代码段一样,这个区域在EA打开的时候开辟出来,直到EA关闭才会删掉。它里面放常量、字符串字面量,以及全局变量,总之这些需要全局使用的东西,就放在这里一直不动了。EA运行过程中,各种函数可以随时调取引用,也可以在自己的地盘上加工它们,但不能修改它们。相当于建筑工地上的永久性仓库,放的是样品、标准件、度量衡之类的东西,一直到房子盖好交付,施工队撤走了,这个样品仓库才会拆除处理掉。而施工过程中只能用、能加工它们的复制品,不能破坏或改变它们本身。

三、栈(Stack)
栈是临时仓库区。类似函数这种能加工处理数据的结构可以在上面划分一块私有地盘,叫做栈帧(Stack Frame)。栈这个词只要你稍微接触一下编程,总有耳闻。
我们常说的比如客栈、货栈之类的词,本质上指的是流动性,客人和货物不会在此长久居住。编程上的栈类似工地上的临时仓库,函数就像是只干某一个具体活儿的小组,系统运行到它的作用域,就轮到这个小组开工。他们自己先搭了一个小仓库,用来放物料,这些物料五花八门,可以是普通的数据类型int、double之类的,也可以是复杂的数组、结构体甚至类。函数/小队把这些数据物料加工完了,得到的半成品也就是返回值被下一个函数/小队接手,该函数/小队功成身退,临走前把临时小仓库和里面的东西全都拆了丢掉。新函数/施工小队又重复这个过程。
所以栈就是存放局部变量、函数参数、函数返回地址的临时区域,而且这些临时区域跟各种函数同生共死,函数的大括号就相当于每个小仓库的墙,墙里墙外互不干扰。函数运行的时候在栈上开辟一个栈帧,往里面放参数,运行完了参数删除,栈帧这个临时小仓库也随着拆了。

四、new和堆(Heap)

堆是独立与栈之外的比较大的存储区域,相当于一个很大的仓库,也可以给你放东西。
“new”的本质是指定在堆这块区域建立一个盒子,里面放东西。虽然“new”也写在某个函数内部,但因为它不是往该函数的私有地盘也就是栈帧里面存数据,而是往堆里存数据,所以当该函数/施工小队收工时,只拆掉了自己在栈上的小仓库,而堆上这小盒子因为是独立于栈之外,函数的结束大括号管不着它,所以它和它里面存放的数据都可以继续存在。
new和普通数据类型int、double之类的功能差不多,都是创建某一个类型的数据空间,我们把这个数据空间比作一个小盒子,相当于某个材料的包装盒,里面只能放一种东西,与仓库的概念不同。只不过创建位置不同,“new”专指往堆区域创建数据类型。
可能有些人会觉得我写的太罗嗦,但这些弯弯绕的概念,对于一个初次接触编程的老登来说,真不是轻易能绕明白,我写的其实就是我自己的认识和学习过程,只希望能帮助更多像我一样初次学习编程越学越晕的朋友。铺垫这么多,终于可以看看原手册上那个例句了:
MyObject* hobject= new MyObject();
这是new的用法格式。还记得前面说的指针吗?我们对照着int *p = &a;这个例句来看:
(1)MyObject* 和int * 是同样的格式,指的是后面的变量是个指针变量,MyObject指这个指针指向的数据的类型是“类”,这个名字是举例这里自己起的,相当于他自定义的类,而int是标准的普通变量,指它指向的数据的类型是“整型”。
(2)hobject和p都是自定义的指针变量的名字。
(3)new和&都是操作符,含义不同,new操作符代表在堆上创建数据并获取其地址,&代表获取后面的变量的地址。
(4)MyObject()和a都是被指向数据的名字,这里都是自定义名字,MyObject()是个带参数构造函数的类,a是个普通整数。
(5)我们知道 XXX * XXX的写法,结果是一处内存地址,所以我们需要用地址获取符&获得某个变量的地址,才能赋值给XXX * XXX。细心的朋友可能会发现,new出来的东西并没有加地址获取符,实际上new除了去堆上创建数据盒子之外,还会返回这个盒子的地址,也就是说,new本身就有返回值,所以能直接给XXX * XXX来赋值。
(6)MyObject* hobject= new MyObject();这一句是写在某个函数内部的,创造了一个变量hobject,这个变量是存在该函数的栈帧/小仓库之中,等该函数运行完了,这个变量联通函数的栈帧/小仓库一并拆除销毁。但在堆上的new MyObject()还在,但也只是在,谁也找不着它了,后续程序想修改或者删除它是办不到的,相当于你白白给自己写了一段没用的代码占用内存。所以这一句后面一般要加一句:
return hobject; //把这个地址结果返回出去,交给调用方复制一份。
(7)理论上new可以在堆上创建任意类型的数据小盒子。但如果只是单个的普通数据类型,实在没有必要,哪怕只有两个函数需要公用一个数据类型,你都可以把他它写成全局变量,对代码的复杂度、程序的反应速度来说没啥影响。但对于那些复杂的数据,比如类、结构体就是new常用的对象。还有个一个动态数组,本来也应该是new的用武之地,只是MQL5默认的把动态数组设计在了堆上,不用你自己new了。
(8)所以new最重要的功能就是实现数据的跨函数调用,某个复杂数据,不合适写成全局变量,又需要被几个函数调用,那就new一个吧。
(9)如果只new不delete,这个在堆上被创建的 new MyObject();会一直存在,就像前面说的,new后面一定要跟一个return,让别的函数可以找到它创建的对象。找到它除了调用之外,还要准备删除。删除代码是这样的:
delete hobject; // 销毁堆上MyObject盒子
hobject = NULL; // 把纸条清空,防止变成野指针,就是数据没了,地址还在的指针,就是手册上说的访问这种无效指针,会导致系统严重关闭。
这段代码写在最后一个需要用这个new数据的函数里,这个函数运行完,new出来的那东西就没了。
(10)如果不写delete或者不写return会怎样?这两种情况都会造成第一个函数new出来的东西一直在堆上存放着。如果只是一行代码,一个结构体,那没什么。但你别忘了我们说MT5的EA是一种高速循环运行的程序,它的代码如果放在OnTick里面,可不是只运行一遍就拉到,行情剧烈的时候,每秒钟都可能运行几十几百几千次。运行一次,就new个,一秒钟就new几十几百几千个,这种程序一旦运行起来,不用十秒钟,就会把内存装满,把电脑卡死机。这叫内存泄露。
最近访问 头像模式
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

麻爪

麻爪 D

如果你想让一个人下地狱,介绍他做交易。
如果你想让一个人在地狱里永不超生,介绍他做EA交易。

Fast跟单EA