English Español Deutsch 日本語 Português
preview
От начального до среднего уровня: Перегрузка

От начального до среднего уровня: Перегрузка

MetaTrader 5Примеры |
360 1
CODE X
CODE X

Введение

В предыдущей статье «От начального до среднего уровня: Плавающая точка», мы говорили об основах работы с числами с плавающей точкой. Я сначала представил этот материал в качестве отправной точки, потому что он крайне важен. На мой взгляд, понимание того, как работают типы float и double, является абсолютно необходимым, чтобы в дальнейшем разбираться в более сложных вопросах.

Хотя содержание статьи и охватывает лишь базовую часть того, что действительно необходимо освоить каждому, кто хочет стать хорошим программистом, этого уже вполне достаточно для того, чтобы перейти к другим темам. Тем не менее, вполне возможно, что в какой-то момент нам придется вернуться к числам с плавающей точкой, но уже на более продвинутом уровне. Однако, всему свое время.

Тем не менее, эта статья, на мой взгляд, уже помогла многим понять, что когда мы имеем дело с программой, а точнее, с торговой платформой, где ошибка означает потерю денег, мы не можем стремиться к абсолютной точности в своих расчетах. Это связано с тем, что сама природа чисел с плавающей точкой не позволяет нам быть абсолютно точными и аккуратными. Все, что мы можем сделать, — это попытаться максимально приблизиться к значению, которое считаем приемлемым или обоснованным, чтобы иметь возможность принять решение о покупке или продаже конкретного актива или финансового инструмента.

Впрочем, эти проблемы в большей степени касаются операторов или пользователей платформы, нежели нашей основной деятельности, которой в данном случае является программирование. Мы, как программисты, должны предупреждать пользователей наших приложений о том, что в расчетах существует определенный риск. И далее, уже сам пользователь или трейдер должен самостоятельно корректировать данные и анализировать, насколько они имеют для него смысл в момент принятия решения о совершении покупки или продажи.

Однако, я все еще не вижу у нас достаточно прочной и хорошо выстроенной базы, чтобы можно было начать говорить о том, как запрограммировать или реализовать индикатор или торгового советника, также называемого роботом. Хотя у нас уже есть хорошее представление о том, что необходимо и что можно сделать, на мой взгляд, в определенные моменты мы все еще можем оказаться со связанными руками. Это потому, что есть вещи, которые все еще не были объяснены. А они в самом деле необходимы в различных ситуациях и для разных целей. Часто именно они позволяют нам делать то, что без соответствующих знаний было бы невозможно реализовать.

И поскольку меня не радует перспектива вдаваться в слишком утомительные и громоздкие подробности в будущих статьях, я хочу создать полную и по-настоящему прочную базу для представления более продвинутых материалов. Это не только сделает статьи более интересными, но и поможет повысить качество предоставляемого контента. Ведь не тратя время на объяснение подобных деталей, я смогу сосредоточиться на передаче еще большего объема знаний в будущих материалах.

Остается еще несколько вещей, которые необходимо объяснить, и которые для многих новичков могут оказаться довольно сложными. И им действительно необходимо уделить время прежде, чем мы перейдем на более высокий уровень.

Так же, как это было сделано в отношении массивов и строк, где я показал, что одно ведет к другому, здесь также необходимо разъяснить один механизм, прежде чем мы перейдем к чему-то более сложному. Причина проста: будет гораздо понятнее, как один, на первый взгляд запутанный, механизм позволяет нам реализовать другой, который многие новички не используют просто потому, что не понимают его, и вынуждены создавать ряд подпрограмм, функций и процедур, которые зачастую совершенно излишни в более продуманном коде. Или, скорее, в коде, написанном программистом, обладающим более глубокими знаниями о том, как реализовать что-либо на том или ином языке. В нашем случае — на MQL5.

Итак, чтобы отделить одно от другого, давайте перейдем к новой теме.


Что такое перегрузка

Если есть что-то, что может полностью сбить с толку начинающих программистов или даже программистов-любителей и сделать невозможным понимание хоть чего-то во фрагменте кода, так это когда в одном и том же коде имеются две функции или процедуры с одинаковым именем. Да, такое действительно бывает, и когда это происходит, менее опытный или не слишком подкованный программист оказывается полностью дезориентирован, не имея возможности ни исправить код, ни улучшить, ни даже создать свой собственный на его основе.

Такого рода вещь, несмотря на то, что непосвященным она может показаться крайне запутанной и совершенно лишенной логики, вполне возможна, допустима и приемлема для компилятора, при условии соблюдения определенных простых правил.

Понимаю и знаю, что многие, должно быть, скажут, что я сошел с ума или полностью потерял нить повествования, поскольку просто не имеют ни малейшего представления о том, как использовать два вызова с различными целями, но при этом содержащими одно и то же имя. Давайте рассмотрим простой пример — действительно очень простой — лишь для того, чтобы показать то, о чем я говорю, и что собираюсь объяснить.

Теперь давайте посмотрим на код, приведенный ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(Sum(10, 25));
07.     Print(Sum(-10, 25.));
08. }
09. //+------------------------------------------------------------------+
10. ulong Sum(ulong arg1, ulong arg2)
11. {
12.     Print(__FUNCTION__, "::", __LINE__);
13.     return arg1 + arg2;
14. }
15. //+------------------------------------------------------------------+
16. double Sum(double arg1, double arg2)
17. {
18.     Print(__FUNCTION__, "::", __LINE__);
19.     return arg1 + arg2;
20. }
21. //+------------------------------------------------------------------+

Код 01

Итак, мой дорогой читатель, я хочу, чтобы вы посмотрели на код 01 и сказали максимально честно, еще до того, как увидите результат выполнения: как именно этот код будет работать? Или, чтобы было понятнее, вы уже должны знать, что когда мы используем вызов Print в коде, мы хотим вывести какое-либо значение на терминале MetaTrader 5. И здесь у нас есть четыре вызова процедуры библиотеки Print.

Итак, перефразирую вопрос, заданный выше: можете ли вы сказать мне, что будет выведено на терминале? Очевидно, вы можете взглянуть на строки шесть и семь и сказать: «Ну, будут выведены значение 35 и значение 15». И это самая очевидная часть операции. Меня же интересует, какая из двух функций будет выполнена: функция Sum в строке десять или функция Sum в строке шестнадцать? Хм, дайте-ка подумать. Упс, но ведь это не скомпилируется, так как у нас две функции с одинаковым именем — и в строке десять, и в строке шестнадцать. Вы думали меня на этом подловить?

Ну что ж, мой дорогой читатель, на самом деле, и вы совершенно правы, здесь действительно мог крыться подвох, поскольку две функции или процедуры НЕ МОГУТ ИМЕТЬ ОДНО И ТО ЖЕ ИМЯ. Это факт, и любой, кто утверждает обратное, лжет или, по крайней мере, что-то скрывает. Тем не менее, это не относится к данному случаю, как бы странно это ни звучало. Две функции Sum, представленные в строках десять и шестнадцать, это НЕ ОДНА И ТА ЖЕ ФУНКЦИЯ. Несмотря на то, что обе они выполняют одну и ту же работу и делают это корректно, компилятор воспринимает их не как одну, а как две разные функции, хотя у них и одинаковое имя.

Правда, теперь я действительно запутался, потому что, насколько мне известно, две функции или процедуры не могут иметь одно и то же имя, когда мы собираемся использовать их в одном и том же коде. И это правда — не могут. Но здесь мы делаем то, что известно как перегрузка функций или процедур.

Проблема перегрузки — одна из тех тем, что приносят мне наибольшее удовольствие при программировании. Потому что, если перегрузка спланирована грамотно, она позволяет нам реализовать гораздо более читаемый код. Хотя существуют и более эффективные способы добиться того же, без понимания, что такое перегрузка, которую вы видите в коде 01, практически — если не сказать невозможно — понять другой механизм, присутствующий в языке MQL5. Но давайте оставим это на потом. Сначала разберемся, что такое перегрузка, и как она работает.

Перегрузка — это именно то, что вы видите в строках десять и шестнадцать этого кода 01. Когда вы его выполните, будет сгенерировано то, что показано на изображении ниже.

Рисунок 01

Нас здесь интересует не значение, которое будет выведено строками шесть и семь, а те значения, которые выделены на изображении. Это и есть данные, которые действительно представляют для нас интерес в данный момент. Обратите внимание, что в одном случае мы ссылаемся на строку 12, а в другом — на строку 18. Почему?

Причина в типе значения, передаваемого в вызовах в строках шесть и семь. Когда компилятор попытается сгенерировать исполняемый файл, он посмотрит на эти значения и скажет: хорошо, это значение соответствует ожидаемому параметру этой функции; то значение соответствует тому другому параметру. И так он разрешит вызовы таким образом, что у нас будет несколько вызовов с одним и тем же именем.

Однако — и это важно — аргументы или ожидаемые параметры НЕ МОГУТ БЫТЬ ОДИНАКОВЫМИ. Они могут иметь некоторое сходство, но должны отличаться по количеству или хотя бы один из них должен быть другого типа. В противном случае, компилятор не сможет понять, куда должен быть направлен код, и это будет считаться ошибкой.

Обратите внимание, что в этом случае и Sum в строке десять, и Sum в строке шестнадцать, используют разные типы. Так как второй аргумент в строке семь отличается от целого числа по типу, компилятор понимает, что в этом случае нужно вызвать функцию, в которой ожидается аргумент с плавающей точкой.

Чтобы закрепить эти знания, давайте рассмотрим еще один случай.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(Sum(10, 25));
07.     Print(Sum((char)-10, 25));
08. }
09. //+------------------------------------------------------------------+
10. ulong Sum(int arg1, ulong arg2)
11. {
12.     Print(__FUNCTION__, "::", __LINE__);
13.     return arg1 + arg2;
14. }
15. //+------------------------------------------------------------------+
16. double Sum(char arg1, ulong arg2)
17. {
18.     Print(__FUNCTION__, "::", __LINE__);
19.     return (double)(arg1 + arg2);
20. }
21. //+------------------------------------------------------------------+

Код 02

Теперь будьте внимательны, потому что код 02 дает тот же результат, что показан на изображении 01. Однако здесь мы имеем дело с чем-то не столь простым, как раньше. Это тот тип кода, который многим не очень опытным программистам кажется слишком запутанным, поэтому они пытаются его изменить, чтобы устранить возникающую путаницу. Однако, поступая так, они в итоге создают себе проблему, потому что результат оказывается не таким, как ожидалось, или как предполагал автор кода.

По этой причине, прежде чем пытаться изменить неизвестный код, мой дорогой читатель, постарайтесь изучить, как он работает. Если возможно, запустите его в контролируемой среде, чтобы можно было понять ход выполнения. Вполне возможно, вы что-то упускаете, и попытка что-то изменить, даже не поняв этого, — одна из самых больших ошибок начинающих программистов.

Теперь давайте разберемся вот в чем: функция Sum, представленная в строках десять и шестнадцать кода 02, по-прежнему возвращает один и тот же тип значения. Обратите внимание, что мы используем явное преобразование в строке 19, чтобы компилятор не жаловался на несовпадение типов.

Но теперь важен следующий момент: заметьте, что различие между функцией Sum в строке десять и Sum в строке шестнадцать заключается в типе первого аргумента, который они принимают. Этот момент обычно многих сбивает с толку, потому что на первый взгляд кажется бессмысленным использовать тип в 4 байта для функции из строки десять и тип в 1 байт для функции из строки шестнадцать.

На самом деле, в данном примере это совершенно не нужно. Однако, программист может по какой-либо причине захотеть работать внутри функций таким образом, что функция в строке шестнадцать действительно должна принимать значение типа char, поскольку тип int может оказаться неподходящим. Напомню, что существует способ решить эту проблему и по-другому, но он будет рассмотрен в другой статье. Здесь мы предполагаем, что вы не знаете, как это сделать, поэтому вы решаете реализовать это так, как показано в коде 02.

Вопрос: это плохо? Нет, мой дорогой читатель, создавать, писать и реализовывать код так, как показано в коде 02 — не плохо, это всего лишь означает, что вам еще стоит немного больше узнать о том, как программировать на MQL5. Но определенно это — неплохо.

Итак, это были простые формы перегрузки. Существует и другая форма, где вместо объявления параметров разных типов мы используем разное количество параметров. Этот случай, возможно, является наиболее распространенным, поскольку, в отличие от C и C++, где мы можем реализовать практически бесконечное количество параметров, не меняя объявление функции или процедуры, здесь, в MQL5, мы не можем этого сделать. Ну, это в тестовом режиме, потому что есть способ это реализовать. Я показал, как — в предыдущей статье, хотя это было лишь вступлением к тому, что на самом деле можно сделать, когда определенные механизмы будут объяснены.

Но давайте снова примем во внимание, что вы, мой дорогой читатель, действительно учитесь и хотите учиться правильно. Так как же будет выглядеть эта другая форма перегрузки? Вы можете увидеть это прямо ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(Sum(10, 25));
07.     Print(Sum(-10, 25, 4));
08. }
09. //+------------------------------------------------------------------+
10. long Sum(long arg1, long arg2)
11. {
12.     Print(__FUNCTION__, "::", __LINE__);
13.     return arg1 + arg2;
14. }
15. //+------------------------------------------------------------------+
16. long Sum(long arg1, long arg2, long arg3)
17. {
18.     Print(__FUNCTION__, "::", __LINE__);
19.     return arg1 + arg2 + arg3;
20. }
21. //+------------------------------------------------------------------+

Код 03

В этом случае результат будет таким, как можно увидеть на следующем изображении.

Рисунок 02

Насколько мне удалось заметить, большинство новичков легче воспринимают такой тип кода, как в примере 03, чем другие, поскольку в нем проще понять, какая функция или процедура будет вызвана в каждом конкретном случае. Многие не считают такой тип реализации, что представлен в коде 03, формой перегрузки. Однако, в литературе многие авторы действительно рассматривают это как разновидность перегрузки, поскольку имя функции остается тем же самым.

Хорошо, я думаю, что теперь стало понятно, что является перегрузкой, а что ею не является. Тем не менее, остается один вопрос: почему это работает, и как компилятору удается различать эти две реализации, лишь на том основании, что используются разные типы? Это кажется не очень логичным, по крайней мере, на первый взгляд.

На самом деле, мой дорогой читатель, это не совсем логично, поскольку, когда мы используем функцию, мы вызываем ее с помощью имени, под которым она была объявлена, а не под каким-то другим. Так почему же это работает? Причина в том, что для того, чтобы код действительно стал исполняемым, компилятору необходимо понимать, как правильно направить поток выполнения. И когда используется перегрузка, в данном случае функций и процедур, компилятор создает уникальное внутреннее имя. Я говорю «внутренне», потому что вы не знаете точно, как оно будет сформировано, хотя, в общем, это делается с использованием некоторых простых правил.

Например: глядя на код 03, в строке десять, компилятор может вызвать эту функцию Sum как Sum_long_long; в то время как функцию из строки шестнадцать он может назвать Sum_long_long_long. Обратите внимание, что, несмотря на кажущуюся простоту, это уже имеет большое значение, поскольку это очень похоже на создание функции с уникальным именем.

То же самое относится к коду 02. Там функцию из строки десять компилятор может интерпретировать как Sum_int_ulong, а из строки шестнадцать — как Sum_char_ulong. Обратите внимание, что теперь все начинает приобретать смысл, и отсюда следует тот факт, что компилятор способен понять, куда должен быть направлен поток выполнения в каждый конкретный момент.

Важное замечание: я использую эту номенклатуру здесь только в дидакических целях, поскольку в зависимости от реализации и того, что задумали разработчики компилятора, эта номенклатура может быть совершенно иной. Просто имейте в виду, что внутренне компилятор делает нечто подобное.

Отлично, мы практически завершаем тему использования перегрузки. Не хватает только двух примеров, которые, на мой взгляд, очень интересно показать. Первый из них приведен ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int v = -5;
07. 
08.     Sum(v, Sum(10, 25));
09.     Print(v);
10. }
11. //+------------------------------------------------------------------+
12. long Sum(long arg1, long arg2)
13. {
14.     Print(__FUNCTION__, "::", __LINE__);
15.     return arg1 + arg2;
16. }
17. //+------------------------------------------------------------------+
18. void Sum(int &arg1, long arg2)
19. {
20.     Print(__FUNCTION__, "::", __LINE__);
21.     
22.     arg1 += (int)arg2;
23. }
24. //+------------------------------------------------------------------+

Код 04

В данном случае у нас есть возможность использовать перегруженную функцию вместе с процедурой. Обратите внимание, что имена у них одинаковые, но благодаря различию в типах, можно перенаправлять поток выполнения в том или ином направлении. Важно отметить, что в зависимости от способа реализации этого кода 04, от того, какие типы аргументов использовались, и как был структурирован сам код — он может дать тот или иной результат. И все из-за того, что мы изменили какую-то мелочь в объявлениях: будь то тип или способ, которым интерпретируется строка восемь.

Обратите внимание на следующий факт: хотя строка восемь, по-видимому, ссылается на функцию, представленную в строке 12, сам факт использования переменной в качестве первого аргумента дает понять, что мы намерены вызвать строку 18. Однако, если тип, объявленный в строке восемь, несовместим с типом, ожидаемым в строке 18, мы можем, по сути, никогда не вызывать строку 18, поскольку все действия будут выполняться в строке 12. По этой причине не рекомендуется использовать перегрузку функций и процедур ненадлежащим образом или без должной осторожности, поскольку это может привести к результатам, сильно отличающимся от ожидаемых.

Тем не менее, в том виде, в каком представлен код 04, при его выполнении мы увидим результат, показанный на следующем изображении.

Рисунок 03

Снова стоит отметить, что такой результат возник исключительно потому, что типы идеально совпали, и компилятор смог правильно понять наше намерение. Однако, если какой-нибудь программист, не изучив и не поняв код, решит, что не хочет использовать тип long в своем коде, так как он 64-битный, и решит использовать только тип int, так как он 32-битный, — посмотрите, что произойдет. После внесения описанных изменений, код 04 превратится в код 05, который можно увидеть ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     int v = -5;
07. 
08.     Sum(v, Sum(10, 25));
09.     Print(v);
10. }
11. //+------------------------------------------------------------------+
12. int Sum(int arg1, int arg2)
13. {
14.     Print(__FUNCTION__, "::", __LINE__);
15.     return arg1 + arg2;
16. }
17. //+------------------------------------------------------------------+
18. void Sum(int &arg1, int arg2)
19. {
20.     Print(__FUNCTION__, "::", __LINE__);
21.     
22.     arg1 += (int)arg2;
23. }
24. //+------------------------------------------------------------------+

Код 05

Обратите внимание, что было сделано совершенно невинное изменение без особого намерения вызвать проблемы, но при попытке создать исполняемый файл выясняется, что компилятор не понимает, что именно реализуется. Сообщение, отображаемое в выходных данных компилятора, показано чуть ниже.

Рисунок 04

То есть, совершенно невинная попытка изменить код на что-то более подходящее, по мнению программиста, в конечном итоге привела к тому, что код стал совершенно непонятным, поскольку компилятор не знает, обращаться ли к строке 12 или к строке 18, когда должна быть выполнена строка восемь.

Однако я хочу, чтобы вы, мой дорогой читатель, поняли здесь одну вещь: ошибка не в строке 8. Ошибка на самом деле заключается в том, что у нас есть два вызова, которые в понимании компилятора абсолютно одинаковы. Обладающий небольшим опытом программист быстро подумает, что проблема в восьмой строке, хотя на самом деле она находится в строке 12 или 18, как объяснялось выше, поскольку компилятор может создавать уникальное внутреннее имя, пытаясь сгенерировать окончательный исполняемый файл.

Такой подход, который здесь может показаться простым для решения, на практике может быть крайне сложным. Потому что один из вызовов может находиться в одном заголовочном файле, а другой — в совершенно другом заголовочном файле, который вообще никак не связан с первым. Это делает ситуацию еще более сложной и запутанной.

И в качестве последнего примера, давайте рассмотрим еще один случай использования перегрузки. Он показан в коде ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     long v = -16,
07.          m[];
08.     bool b;
09.     
10.     Add(m, Sum(10, 25));
11.     Print("Return: ", b = Sum(v, m));
12.     if (b) Print("Value: ", v);
13.     ArrayFree(m);
14. }
15. //+------------------------------------------------------------------+
16. void Add(long &arg[], long value)
17. {
18.     ArrayResize(arg, arg.Size() + 1);
19.     arg[arg.Size() - 1] = value;
20. }
21. //+------------------------------------------------------------------+
22. long Sum(long arg1, long arg2)
23. {
24.     Print(__FUNCTION__, "::", __LINE__);
25.     return arg1 + arg2;
26. }
27. //+------------------------------------------------------------------+
28. bool Sum(long &arg1, const long &arg2[])
29. {
30.     Print(__FUNCTION__, "::", __LINE__);
31. 
32.     if (arg2.Size() == 0)
33.         return false;
34. 
35.     for (uchar c = 0; c < arg2.Size(); c++)
36.         arg1 += arg2[c];
37.     
38.     return true;
39. }
40. //+------------------------------------------------------------------+

Код 06

Этот случай действительно очень интересный и имеет множество практических последствий. Потому что нередко, по крайней мере в среде MQL5, где цель заключается в создании механизмов для графической интерпретации цен и сделок, мы реализуем код, основное назначение или цель которого — выполнить некоторую факторизацию с использованием нескольких котировок. Обычно такого рода факторизация каким-либо образом связана с разработкой конкретного индикатора или торговой модели, предназначенной для использования в советнике, с целью формирования наглядного графического сигнала и привлечения внимания трейдера или пользователя платформы MetaTrader 5 — к открытию или закрытию позиции, либо, по крайней мере, к определенному движению, которое может в какой-то момент произойти.

Я понимаю, что многие, особенно новички, горят желанием что-то создать и сразу увидеть, как это исполняется. Однако прежде, чем мы сможем это сделать, необходимо хорошенько разобраться вот с такого рода вещами, которые мы сейчас рассматриваем в этих статьях. Здесь материал более базовый и направлен как раз на создание прочного фундамента, чтобы впоследствии работать с чем-то более сложным и ориентированным на конкретную задачу. Поскольку я не хочу тратить много времени на объяснение простых деталей в будущем, я создаю эту базу знаний сейчас. Таким образом, вы, мой дорогой читатель, сможете адаптировать или даже улучшить то, что увидите в статьях с более специализированным содержанием, не только моих, но и любого другого программиста, чью идею или код вы захотите использовать.

Итак, в этом коде 06 у нас есть как раз пример перегрузки, где в одном случае мы используем дискретное значение — весьма интересным способом, а в другом — набор значений, содержащихся в массиве данных. Однако, несмотря на внешнюю простоту, у нас нередко возникают проблемы с вычислениями, которые выполняются, из-за небольших ошибок в понимании некоторых базовых концепций. Среди этих ошибок — тот факт, что зачастую мы не обращаем внимания на начальные значения параметров, передаваемых в функцию. Такого рода ошибки порой бывает непросто отследить и устранить — по крайней мере, до тех пор, пока мы не поймем настоящую причину сбоя.

Но подобные вещи легче понять на практике, и по мере накопления опыта вы все реже будете совершать такие ошибки, а если они и будут происходить, то лишь вследствие невнимательности во время реализации.

А теперь давайте разберемся, что у нас здесь, в коде 06. При выполнении кода, показанного выше, вы получите следующий результат, представленный ниже.

Рисунок 05

А теперь поймите следующее, мой дорогой читатель. Как уже было сказано ранее, нередко у нас возникают проблемы при множественной факторизации. Это связано с тем, что мы используем неправильное количество элементов при факторизации. Чтобы этого избежать, мы отказываемся от возврата значения из функции и вместо этого возвращаем признак. Он будет указывать, следует ли принимать факторизованное значение или нет. Поэтому, в строке 32 мы проверяем именно это. Обратите внимание, что функция Sum все еще перегружена, поскольку в строке 22 она имеет то же имя, что и в строке 28. Однако факт, что возвращаемое значение имеет другую цель, не отменяет того, что функция все равно перегружена.

Подобные вещи часто могут сбивать с толку, особенно если мы смотрим только на строки, в которых используется функция. Посмотрите на строки десять и одиннадцать — вы явно заметите, что то, что возвращается, не совсем логично. Поэтому, если у вас возникают сомнения относительно работы кода, постарайтесь изучить его детали. Не думайте, что только потому, что у функции или процедуры такое же имя, как у другой, уже знакомой вам — они обязательно работают одинаково. Это не всегда так.

И напоследок, мы можем внести небольшое изменение в код 06. Оно показано в приведенном ниже фрагменте.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     long v = -16,
07.          m[];
08.     bool b;
09.     
10. //    Add(m, Sum(10, 25));
11.     Print("Return: ", b = Sum(v, m));
12.     if (b) Print("Value: ", v);
13.     ArrayFree(m);
14. }
15. //+------------------------------------------------------------------+
                   .
                   .
                   .

Фрагмент 01

Обратите внимание, что в этом фрагменте я удаляю одну строку из кода, превращая ее в комментарий. В данном случае — это строка десять. Но это простое изменение, которое, казалось бы, всего лишь означает, что мы не добавили нужное количество элементов для факторизации, приведет к тому, что результат будет таким, как показано ниже.

Рисунок 06

Поэкспериментируйте с внесением других изменений в код 06, чтобы получить другие типы понимания, так как это очень поможет вам в будущем.


Заключительные соображения

Возможно, для начинающих программистов эта статья оказалась самой запутанной, поскольку здесь я показал, то не всегда в одном и том же коде все функции и процедуры будут иметь уникальные имена. Да, мы вполне можем использовать функции и процедуры с одинаковым именем — и это называется перегрузкой. Несмотря на то, что это допустимо, следует проявлять осторожность при использовании такой практики. Потому что она часто делает код не только сложным для реализации, но и для последующего исправления.

Надеюсь, что эта статья послужит вам разъяснением и сделает более внимательными в отношении того, чтобы не предполагать автоматически, что уже известная функция или процедура обязательно будет вести себя так же, как другая функция или процедура, реализованная с использованием перегрузки.

В приложении я оставлю фрагменты кода, рассмотренного в этой статье. Постарайтесь изучить этот материал, внося изменения в коды из приложения, чтобы понять, как перегрузка может повлиять как на ваше понимание кода, так и на потенциальные ошибки, которые могут возникнуть из-за неправильного использования перегрузки. Лучше научиться справляться с ошибками, пока механизмы еще просты, чем пытаться разобраться с ошибкой в более сложной системе. Поэтому изучите сейчас вопрос перегрузки, пока все относительно просто. Ведь по мере продвижения вперед, все только будет становиться сложнее.

Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15642

Прикрепленные файлы |
Anexo.zip (1.98 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Aliaksandr Kazunka
Aliaksandr Kazunka | 27 июн. 2025 в 21:36
в новых билдах терминала две функции с одинаковым именем выдает ошибку компиляции
Алгоритм эхолокации дельфинов — Dolphin Echolocation Algorithm (DEA) Алгоритм эхолокации дельфинов — Dolphin Echolocation Algorithm (DEA)
В этой статье мы подробно рассмотрим алгоритм DEA — метаэвристический метод оптимизации, вдохновленный уникальной способностью дельфинов находить добычу с помощью эхолокации. От математических основ до практической реализации на MQL5, от анализа до сравнения с классическими алгоритмами — детально разберем, почему этот относительно молодой метод заслуживает места в арсенале тех, кто сталкивается с задачами оптимизации.
Подробная информация о торговле на основе объема: Подтверждение тренда Подробная информация о торговле на основе объема: Подтверждение тренда
Усовершенствованный метод подтверждения тренда сочетает в себе ценовое движение, анализ объема и машинное обучение для выявления подлинных изменений на рынке. Для подтверждения сделки требуются как ценовые пробои, так и скачки объема (на 50% выше среднего), а для дополнительного подтверждения используется нейронная сеть LSTM. Система использует определение размера позиции на основе ATR и динамическое управление рисками, что позволяет ей адаптироваться к различным рыночным условиям и одновременно отфильтровывать ложные сигналы.
Возможности Мастера MQL5, которые вам нужно знать (Часть 48): Аллигатор Билла Вильямса Возможности Мастера MQL5, которые вам нужно знать (Часть 48): Аллигатор Билла Вильямса
Аллигатор, детище Билла Вильямса, представляет собой универсальный индикатор определения тренда, который дает четкие сигналы и часто сочетается с другими индикаторами. Классы Мастера MQL5 позволяют нам тестировать различные сигналы на основе паттернов, что позволяет нам рассмотреть и этот индикатор.
Установка MetaTrader 5 и других приложений от MetaQuotes на HarmonyOS NEXT Установка MetaTrader 5 и других приложений от MetaQuotes на HarmonyOS NEXT
Приложения от MetaQuotes, включая платформы MetaTrader 5 и MetaTrader 4, можно установить на устройства с операционной системой HarmonyOS NEXT с помощью компонента DroiTong. В статье представлено пошаговое руководство для установки программ на телефон или ноутбук.