English Русский Español Deutsch 日本語 Português
preview
从基础到中级:模板和类型名称(四)

从基础到中级:模板和类型名称(四)

MetaTrader 5示例 |
141 0
CODE X
CODE X

概述

此处提供的材料仅用于教育目的。它不应以任何方式被视为最终应用程序。其目的不是探索所提出的概念。

在上一篇文章:“从基础到中级:模板和类型名称(三) ”中,我们开启了一个让很多新手感到特别有挑战性的话题。这是因为许多人还没有理解一个对 MQL5 程序员非常重要的概念:模板的概念。由于我了解到许多读者对编程知之甚少,我试图使材料尽可能具有教育性。

因此,我们相当突然地结束了上一篇文章,它以一个错误的图片和一个没有生成可执行文件的代码结束。我知道很多人可能会失望地看到这样的文章。然而,我刚刚开始向您介绍一个当您第一次了解它时非常困难的主题:类型重载的主题。事实上,我们目前创建的不是类型重载,而是一个模板类型,它允许编译器为我们需要处理的每种情况生成正确的类型。

由于原则上文章中提供的任何代码都应该可以工作,因此我们在解释中做了一些小小的限制。但我试图解释一下,这样你就可以理解,当我们实现它时,代码并不总是有效的。我知道很多人都想学习如何解决代码中的问题。然而,他们中的绝大多数人无法解决问题,仅仅是因为他们对这种资源或编程语言都没有正确的想法。如果没有这个,就很难解释如何解决某些类型的任务,这些任务对专业程序员来说微不足道,但对初学者来说却是一个大问题。

到目前为止,我们一直在考虑使用模板重载函数和过程,但当应用于其他类型的应用程序时,事情会变得更加复杂,如前一篇文章末尾所示。那么让我们开始一个新的话题。


使用模板类型

事实上,要应用的概念很简单。然而,很难以正确实施这一概念的方式对其进行可视化。我们将从上一篇文章中已经看到的内容开始。但在此之前,我们将考虑有效且可以编译的源代码。它就在下面。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06. //+----------------+
07. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
08.                         {                                                           \
09.                             tmp = X.u8_bits[i];                                     \
10.                             X.u8_bits[i] = X.u8_bits[j];                            \
11.                             X.u8_bits[j] = tmp;                                     \
12.                         }
13. //+----------------+
14.     union un_01
15.     {
16.         ulong value;
17.         uchar u8_bits[sizeof(ulong)];
18.     };
19. 
20.     {
21.         un_01 info;
22. 
23.         info.value = 0xA1B2C3D4E5F6789A;
24.         PrintFormat("The region is composed of %d bytes", sizeof(info));
25.         PrintFormat("Before modification: 0x%I64X", info.value);
26.         macro_Swap(info);
27.         PrintFormat("After modification : 0x%I64X", info.value);
28.     }
29. 
30.     {
31.         un_01 info;
32. 
33.         info.value = 0xCADA;
34.         PrintFormat("The region is composed of %d bytes", sizeof(info));
35.         PrintFormat("Before modification: 0x%I64X", info.value);
36.         macro_Swap(info);
37.         PrintFormat("After modification : 0x%I64X", info.value);
38.     }
39. }
40. //+------------------------------------------------------------------+

代码 01

当代码 01 在 MetaTrader 5 中编译并执行时,将获得以下结果。

图 01

显然,这个结果并不完全正确。这与该图中突出显示的区域 01 相关。但根据具体的用例,这个结果将是正确的。但这不是我们想要的,我们希望第 33 行声明的值是两个字节宽。然而,正是因为联合体有 8 个字节宽,我们被迫使用这种类型的声明,这使得最终结果完全不够,正如您在图 01 中看到的那样。

但在上一篇文章中,我们已经直接创建了模板类型。即使您看到的代码没有错误,但缺少了一些东西,也很难解释为什么所有事情都应该按照我们的方式执行。至此,我决定结束这篇文章,给你一个冷静思考和探索这个问题的机会。目标是试图理解为什么代码不起作用。在这里,我们将弄清楚如何正确操作,以及为什么代码应该以非常具体的方式实现,以便编译器理解应该做什么。

下一步自然是用稍微不同的代码替换代码 01。这是在我们实现上一篇文章所展示的内容之前完成的。其结果为以下代码:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06. //+----------------+
07. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
08.                         {                                                           \
09.                             tmp = X.u8_bits[i];                                     \
10.                             X.u8_bits[i] = X.u8_bits[j];                            \
11.                             X.u8_bits[j] = tmp;                                     \
12.                         }
13. //+----------------+
14. 
15.     {
16.         union un_01
17.         {
18.             ulong value;
19.             uchar u8_bits[sizeof(ulong)];
20.         };
21.         
22.         un_01 info;
23. 
24.         info.value = 0xA1B2C3D4E5F6789A;
25.         PrintFormat("The region is composed of %d bytes", sizeof(info));
26.         PrintFormat("Before modification: 0x%I64X", info.value);
27.         macro_Swap(info);
28.         PrintFormat("After modification : 0x%I64X", info.value);
29.     }
30. 
31.     {
32.         union un_01
33.         {
34.             ushort value;
35.             uchar u8_bits[sizeof(ushort)];
36.         };
37. 
38.         un_01 info;
39. 
40.         info.value = 0xCADA;
41.         PrintFormat("The region is composed of %d bytes", sizeof(info));
42.         PrintFormat("Before modification: 0x%I64X", info.value);
43.         macro_Swap(info);
44.         PrintFormat("After modification : 0x%I64X", info.value);
45.     }
46. }
47. //+------------------------------------------------------------------+

代码 02

好吧,当我们运行代码 02 时,最终结果将是我们在下图中看到的。

图 02

请注意,在图 02 中我们得到了我们想要实现的结果。也就是说,现在我们有一个要显示的类型值,需要 8 个字节。另一个值也会显示出来,需要两个字节,正如预期的那样。然而,看看它是如何完成的。即使它正在运行,我们也会通过创建代码并对其进行适当的更改来重载自己。随着代码的增长和变得更加复杂,这增加了出错的机会,因为我们必须向其中添加越来越多的新元素。

正如你所看到的,我们正在做一些非常简单的事情,但代码已经开始有点令人困惑。正是在这一点上,出现了使用类型模板的想法。原因是第 15 行和第 29 行之间的代码块与第 31 行和第 45 行之间的代码块之间的唯一区别在于联合内部定义的类型。您可以在代码 02 的第 18 行和 34 行看到它。

因此,我们开始创建一个模板,其主要目的是简化相同的代码 02,因为需要执行的联合在其中被重载。因此,所提出的概念的应用用于为函数或过程创建模板,在这种情况下,这些模板可以被重载。结果我们得到这样的代码:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13. //+----------------+
14. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
15.                         {                                                           \
16.                             tmp = X.u8_bits[i];                                     \
17.                             X.u8_bits[i] = X.u8_bits[j];                            \
18.                             X.u8_bits[j] = tmp;                                     \
19.                         }
20. //+----------------+
21. 
22.     {
23.         un_01 info;
24. 
25.         info.value = 0xA1B2C3D4E5F6789A;
26.         PrintFormat("The region is composed of %d bytes", sizeof(info));
27.         PrintFormat("Before modification: 0x%I64X", info.value);
28.         macro_Swap(info);
29.         PrintFormat("After modification : 0x%I64X", info.value);
30.     }
31.     
32.     {
33.         un_01 info;
34. 
35.         info.value = 0xCADA;
36.         PrintFormat("The region is composed of %d bytes", sizeof(info));
37.         PrintFormat("Before modification: 0x%I64X", info.value);
38.         macro_Swap(info);
39.         PrintFormat("After modification : 0x%I64X", info.value);
40.     }
41. }
42. //+------------------------------------------------------------------+

代码 03

这就是混乱开始的地方。这一切都与以下内容有关:代码 03 试图创建代码 02 中显示的内容,但使用与代码 01 中显示的内容非常相似的东西。但是,在编译代码 03 时,编译器输出以下错误消息。

图 03

这里肯定有问题,但乍一看却不合理。因为,乍一看,我们正确地声明了模板。那么,代码 03 有什么问题阻止了它的编译呢?如果它不能编译,那么编译器就不理解需要做什么。

嗯,我知道很多人总是试图解决编译器向我们报告和指出的问题。这一点至关重要。但在这种情况下,编译器输出的这些消息(如图 03 所示)并没有向我们指出问题所在。

然而,我们在第一篇文章中解释了一个关于变量和常量的概念。这一概念现在非常重要,以便我们了解如何在这种情况下采取行动。正如你所看到的,在代码 03 的第 07 行我们声明了一个变量,对吗?我只是问:我们在第 07 行声明了什么变量?这就是重点。如果我和其他程序员都不知道,那么编译器怎么知道呢?

好吧,你可以回答我:“在第 25 行中,我们指定我们想要一个宽度为 8 个字节的类型,在第 35 行中,我们指定我们想要一个宽度为 2 个字节的类型。”这是正确的,但这并没有告诉编译器要使用哪种类型的变量。请注意,在第 25 行和第 35 行我们没有声明变量。我们给它分配一个值。该声明包含在第 23 行和第 33 行。你现在看到问题了吗?

但还有另一个更严重的问题,它导致了图 03 中显示的所有错误消息。事实是,尽管声明出现在第 23 行和第 33 行,但它声明了一些编译器不知道的内容,即第 07 行中的变量类型。请注意,第 23 行和第 33 行指的是第 05 行声明的类型。换句话说,是一个联合。

然而,这个联合只是一个模板。因此,要使用的数据类型由编译器决定。由于它不知道使用哪种数据类型,所有这些错误消息都会出现。但现在有一个概念应该被接受。如果你了解模板在函数和过程中的使用方式,你可能知道在某个时刻会声明一个变量。当函数或过程被重载时,编译器已经知道数据类型,因此它可以创建适当的过程。

了解了这一点,您可能会问:我如何向编译器指出要使用哪种数据类型?我们通常使用数据类型后跟变量名。由于我们在代码 03 的第 23 行和 33 行中执行了此操作,所以我不知道如何解决这个问题。如果你已经达到了这一点,并弄清楚了所有这些概念,那么是时候看看如何解决这个问题,以便能够使用模板和重载不同的类型了。为此,MQL5 使用了一种特殊的变量声明,可以是局部的或全局的,这些变量已经存在于 C 和 C++ 语言中。但请记住:最重要的是理解概念,而不仅仅是记住如何去做。解决方案如下。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13. //+----------------+
14. #define macro_Swap(X)   for (uchar i = 0, j = sizeof(X) - 1, tmp; i < j; i++, j--)  \
15.                         {                                                           \
16.                             tmp = X.u8_bits[i];                                     \
17.                             X.u8_bits[i] = X.u8_bits[j];                            \
18.                             X.u8_bits[j] = tmp;                                     \
19.                         }
20. //+----------------+
21. 
22.     {
23.         un_01 <ulong> info;
24. 
25.         info.value = 0xA1B2C3D4E5F6789A;
26.         PrintFormat("The region is composed of %d bytes", sizeof(info));
27.         PrintFormat("Before modification: 0x%I64X", info.value);
28.         macro_Swap(info);
29.         PrintFormat("After modification : 0x%I64X", info.value);
30.     }
31.     
32.     {
33.         un_01 <ushort> info;
34. 
35.         info.value = 0xCADA;
36.         PrintFormat("The region is composed of %d bytes", sizeof(info));
37.         PrintFormat("Before modification: 0x%I64X", info.value);
38.         macro_Swap(info);
39.         PrintFormat("After modification : 0x%I64X", info.value);
40.     }
41. }
42. //+------------------------------------------------------------------+

代码 04

看看我们现在正在做什么,因为这是一个非常微小的变化。但是,当尝试编译代码 04 时,我们看到以下内容。

图 04

也就是说,一些看似无关紧要、目前没有多大意义的事情却使得代码可以编译。我们可以在下图中看到执行代码 04 的结果。

图 05

多么美丽和神奇的事情,不是吗?但是这里发生了什么,为什么代码 04 有效,而代码 03 无效,为什么我们在第 23 行和第 33 行中做出这个奇怪的声明?“现在我完全不知所措了,目瞪口呆。因为我完全不明白这里发生了什么。”

好的,让我们看看这里发生了什么,以及为什么必须按照第 23 行和第 33 行的方式进行声明。因此,这是我们关于模板和类型名称的第四篇文章。在第二篇文章中,我们讨论了如何强制编译器使用特定的数据类型,以便该类型与函数或过程接收到的参数相匹配。在这种情况下,类型转换发生在值赋值过程中。为此,我们使用了显式类型转换,将目标类型括在括号中。正如您在其他代码中可能注意到的那样,这种情况经常发生在不同的时间。然而,为已声明的变量赋值是一回事,为尚未声明的变量赋类型是另一回事。

由于模板的目的是创建我们将来可以使用的函数、过程或数据类型的模型,因此它们将附带类型名声明。这就是问题所在。正如之前的文章所解释的,typename 声明中附带的字母 T 实际上是用于稍后定义某些内容的标签。因此,当编译器替换 T 时,我们将得到我们需要使用的类型定义。并且编译器将能够正确创建代码。因此,当我们在第 23 行和第 33 行的声明中以这种方式声明类型时,我们实际上是告诉编译器使用哪种数据类型,而不是类型名声明中附带的 T。

由于我们还没有详细讨论如何使用此声明,因此您可能需要一段时间才能理解它。然而,正如你所看到的,它是有效的。因此,声明应该这样设计。

如果你觉得这很有趣,那么你会喜欢接下来的内容,因为现在我们要将宏从代码 04 转换为函数或过程。目标是让代码 04 继续工作并获得图 05 中显示的结果。但是,由于这意味着以更高级的形式应用当今的材料,我们将单独考虑这个问题。


当你可以简化它时,为什么要把它复杂化?

你们中的许多人可能会认为我们在这里的行为不合逻辑,这种材料太先进了,我们不必学习如何去做。事实上,我必须同意这一说法。如果你只知道如何创建函数和过程、声明变量和使用一些基本语句,你几乎可以创建任何东西。我们拥有的工具和资源越多,实施和创建它们就越容易。只需一颗钉子,你甚至可以建造一个粮仓升降机。这项工作已经完成。但如果我们能将更多的资源用作 “钉子”,事情就会容易得多。

现在我们将创建一个函数来替换代码 04 第 14 行定义的宏。这将会非常有趣。但首先,我必须提醒大家,我们的目标是学习。在试图理解我们在这里做什么之前,有必要了解上一个主题中做了什么。只有这样,一切才有意义。

下一步,让我们从最初的想法开始。也就是把代码 04 中的宏去掉,尝试把它变成一个函数。但在我们这样做之前,让我们创建一个在我看来更容易理解的程序。然后,您将能够创建以下代码,如下所示。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     {
14.         un_01 <ulong> info;
15. 
16.         info.value = 0xA1B2C3D4E5F6789A;
17.         PrintFormat("The region is composed of %d bytes", sizeof(info));
18.         PrintFormat("Before modification: 0x%I64X", info.value);
19.         Swap(info);
20.         PrintFormat("After modification : 0x%I64X", info.value);
21.     }
22.     
23.     {
24.         un_01 <ushort> info;
25. 
26.         info.value = 0xCADA;
27.         PrintFormat("The region is composed of %d bytes", sizeof(info));
28.         PrintFormat("Before modification: 0x%I64X", info.value);
29.         Swap(info);
30.         PrintFormat("After modification : 0x%I64X", info.value);
31.     }
32. }
33. //+------------------------------------------------------------------+
34. void Swap(un_01 &arg)
35. {
36.     for (uchar i = 0, j = sizeof(arg) - 1, tmp; i < j; i++, j--)
37.     {
38.         tmp = arg.u8_bits[i];
39.         arg.u8_bits[i] = arg.u8_bits[j];
40.         arg.u8_bits[j] = tmp;
41.     }
42. }
43. //+------------------------------------------------------------------+

代码 05

当我们尝试编译代码 05 时,令我们惊讶和失望的是,屏幕上出现了以下结果。它就在下面提供。

图 06

再来一次?这不再有趣了。冷静点,没有理由恐慌或绝望。迄今为止,该问题与上一主题中提到的问题非常相似。但在这种情况下,解决方案是不同的。请注意以下几点:在第 19 行和第 29 行中,该过程在第 34 行被调用。到目前为止,一切都很好。问题是,该过程期望的数据类型与第 05 行中定义的数据类型完全相同。由于此类型是一个模板,编译器不知道如何处理它,因为我们无法告诉它使用哪种数据类型。

“但是等等,你是什么意思?我们在第 14 行和第 24 行声明了数据类型。”是的,但是在 14 和 24 行中我们是在局部定义了要使用的数据类型。然而,这并没有从第 34 行传递给过程,正是因为它是一种复杂类型,而不是基础类型,就像过去所有内容都按原样传递一样。还有一件小事,由于数据类型允许类型重载,因此当尝试将其传递到函数或过程内部时,我们必须确保该函数或过程也可以被重载。这就是为什么我说这样的时刻很有趣。

也就是说,由于可以在 OnStart 过程中重载参数,因此接收相同类型数据的函数或过程也必须重载。因此,一切都是合适的,编译器将能够理解代码以创建所需的可执行文件。

一旦我们理解了这一点,并知道如何创建一个使用模板的重载函数,就可以使用代码 05 并将其修改为创建代码 06,我们将在下面看到。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     {
14.         un_01 <ulong> info;
15. 
16.         info.value = 0xA1B2C3D4E5F6789A;
17.         PrintFormat("The region is composed of %d bytes", sizeof(info));
18.         PrintFormat("Before modification: 0x%I64X", info.value);
19.         Swap(info);
20.         PrintFormat("After modification : 0x%I64X", info.value);
21.     }
22.     
23.     {
24.         un_01 <ushort> info;
25. 
26.         info.value = 0xCADA;
27.         PrintFormat("The region is composed of %d bytes", sizeof(info));
28.         PrintFormat("Before modification: 0x%I64X", info.value);
29.         Swap(info);
30.         PrintFormat("After modification : 0x%I64X", info.value);
31.     }
32. }
33. //+------------------------------------------------------------------+
34. template <typename T>
35. void Swap(un_01 &arg)
36. {
37.     for (uchar i = 0, j = sizeof(arg) - 1, tmp; i < j; i++, j--)
38.     {
39.         tmp = arg.u8_bits[i];
40.         arg.u8_bits[i] = arg.u8_bits[j];
41.         arg.u8_bits[j] = tmp;
42.     }
43. }
44. //+------------------------------------------------------------------+

代码 06

太好了!现在我们有了正确的代码。编译器最终将能够弄清楚我们正在做什么。因此,我们将要求编译器创建一个可执行文件,因为已经实现了使用模板的重载,如代码 06 所示。然而,不幸的是,编译器告诉我们下面看到的内容。

图 07

“现在这个系统肯定在欺负我们,沒有其他的。我很抱歉,但我不明白为什么会发生这种事。”亲爱的读者,别担心。我们正在做的事情迫使许多人每次都以同样的方式创建代码,将本可以更容易、更少出错的事情变成可怕的事情,并重复各种本可以改进的要点。

实际上,创建模板并不是最简单的任务。因此,很难看到使用此类资源的代码。然而,理解这一点将在很多方面对我们有所帮助,因为从编程的角度来看,它大大简化了代码,因为它将所有的复杂性都转移到了编译器上。现在让我们弄清楚为什么会发生这个错误。起初,这样的事情让我们很痛苦。我一直在尝试学习如何正确地做到这一点,因为类似的元素经常在 C 和 C++ 中使用。由于 MQL5 基于 C 和 C++ 中的许多概念,因此一旦我学会了如何使用这些语言,学习 MQL5 就变得像 ABC 一样简单。几乎就像小孩子的游戏。但很难理解我们在这里解释的内容。

当心,虽然编译器报告错误在第 19 行和第 29 行,但它把我们带到了错误的地方。这不是编译器的错,而是我们的错。让我解释一下原因。你还记得在上一个主题中,有必要采取措施,以便编译器能够理解我们使用的数据吗?我们创建了一个声明,可以在代码 06 的第 14 行和第 24 行中看到。

如果这在 OnStart 过程中正确发生,那么这在 Swap 过程中就不会发生。人们甚至可能认为 Swap 过程的模板声明是正确的,但这并不完全正确。这在之前的三篇文章中已经讨论过。但这里很难看出错误。如果你看不到错误在哪里,这是因为你还没有弄清楚函数或过程模板是如何处理重载的。

请注意,在第 34 行声明模板时,我们表明我们将使用 T 类型来创建重载,对吗?现在再看一下第 35 行。哪个参数(在本例中是唯一的一个)具有这种 T 类型?没有接受此 T 类型的参数。也就是说,虽然声明说我们正在创建一个模板,但实际上并没有使用它。现在让我们回到前面的文章,看看这个声明是如何做出的。你会看到类似这样的内容:

                   .
                   .
                   .
15. template <typename T>
16. T Averange(const T &arg[])
                   .
                   .
                   .

片段 01

在代码片段 01 中,请注意以下事实:T 类型在第 15 行声明,但随后我们在第 16 行立即使用它。这是必要的,以便编译器创建函数或过程时参数可以使用该类型。如果不这样做,编译器将不知道如何继续。代码 06 中发生了同样的事情:我们有一个声明,但我们不使用它。换句话说,我们没有告诉编译器如何使用声明。为此,我们应该再次更改代码 06,以便编译器理解我们想要做什么。下面是可以工作的代码。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T>
05. union un_01
06. {
07.     T value;
08.     uchar u8_bits[sizeof(T)];
09. };
10. //+------------------------------------------------------------------+
11. void OnStart(void)
12. {
13.     {
14.         un_01 <ulong> info;
15. 
16.         info.value = 0xA1B2C3D4E5F6789A;
17.         PrintFormat("The region is composed of %d bytes", sizeof(info));
18.         PrintFormat("Before modification: 0x%I64X", info.value);
19.         Swap(info);
20.         PrintFormat("After modification : 0x%I64X", info.value);
21.     }
22.     
23.     {
24.         un_01 <ushort> info;
25. 
26.         info.value = 0xCADA;
27.         PrintFormat("The region is composed of %d bytes", sizeof(info));
28.         PrintFormat("Before modification: 0x%I64X", info.value);
29.         Swap(info);
30.         PrintFormat("After modification : 0x%I64X", info.value);
31.     }
32. }
33. //+------------------------------------------------------------------+
34. template <typename T>
35. void Swap(un_01 <T> &arg)
36. {
37.     for (uchar i = 0, j = sizeof(arg) - 1, tmp; i < j; i++, j--)
38.     {
39.         tmp = arg.u8_bits[i];
40.         arg.u8_bits[i] = arg.u8_bits[j];
41.         arg.u8_bits[j] = tmp;
42.     }
43. }
44. //+------------------------------------------------------------------+

代码 07

当然,现在我们有一个可用的代码,它能提供与图 05 相同的结果。但我想问你:你是否已经弄清楚代码 07 为何有效?想象一下,在编程考试中,你被要求使用函数或过程创建一个与代码 07 生成相同结果的代码。

但不使用模板,而必须使用代码 07 第 04 行声明的联合模板。你能做到吗?为了找到一份程序员的工作,你能解决这个问题吗?或者你会说这是不可能的?在某些时候,我们提到,在这种情况下,过程或函数必须使用要生成的模板。

有些概念和细节让事情变得非常有趣。例如,我们可以用另一种方式创建代码 07 来直接管理重载,而无需为此使用模板。由于理解这些事情需要适当的解释,我们将在下一篇文章中考虑这个问题。


最后的探讨

在本文中,我们讨论了如何管理模板并以更通用和精确的方式创建模板。因为我知道这些内容很难立刻理解,因为我在编程生涯初期学习 C 和 C++ 时自己也经历过这些,所以我请您耐心等待。我建议你练习这些文章中展示的内容,最重要的是,试着理解所提出的概念。如果你理解它们,你几乎可以创建任何代码,而且比那些坚持记忆代码片段和编程语言语法的人要容易得多。因此,要冷静地实践和学习应用的内容。我们将在下一篇文章中与您见面。

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/15670

附加的文件 |
Anexo.zip (3.9 KB)
在 MQL5 中创建交易管理面板(第九部分):代码组织(三):通信模块 在 MQL5 中创建交易管理面板(第九部分):代码组织(三):通信模块
欢迎参与本次深度讨论,我们将揭示 MQL5 界面设计的最新进展,着重介绍重新设计的通信面板,并继续我们关于使用模块化原则构建新管理面板的系列文章。我们将逐步开发 CommunicationsDialog 类,并详细解释如何从 Dialog 类进行继承。此外,在我们的开发过程中,还将利用数组(arrays)和 ListView 类。获取可行的方案,以提升您的 MQL5 开发技能——请阅读本文,并在评论区加入讨论!
MQL5 交易工具包(第 7 部分):使用最近取消的挂单函数扩展历史管理 EX5 库 MQL5 交易工具包(第 7 部分):使用最近取消的挂单函数扩展历史管理 EX5 库
了解如何完成历史管理 EX5 库中最终模块的创建,重点关注负责处理最近取消的挂单的函数。这将为您提供使用 MQL5 有效检索和存储与已取消挂单相关的关键详细信息的工具。
MQL5自动化交易策略(第十四部分):基于MACD-RSI统计方法的交易分层策略 MQL5自动化交易策略(第十四部分):基于MACD-RSI统计方法的交易分层策略
本文将介绍一种结合MACD和RSI指标与统计方法的交易分层策略,通过MQL5实现动态自动化交易。我们将探讨这种级联式策略的架构设计,通过关键代码段详解其实现方式,并指导读者如何进行回测以优化策略表现。最后,我们将总结该策略的潜力,并为自动化交易的进一步优化奠定基础。
从基础到中级:模板和类型名称(三) 从基础到中级:模板和类型名称(三)
在本文中,我们将讨论该主题的第一部分,这对初学者来说并不容易理解。为了避免更加困惑并正确解释这个话题,我们将把解释分为几个阶段。我们将把这篇文章用于第一阶段。然而,尽管在本文末尾,我们似乎已经陷入僵局,但事实上,我们将朝着另一种情况迈出一步,这将在下一篇文章中得到更好的理解。