5. Funkcje własne.

5.10. Przeciążanie funkcji: część 1

Podobnie jak w języku programowania C++ w MQL4 istnieje możliwość przeciążania, mówiąc inaczej przeładowania funkcji. Zjawisko to polega na możliwości utworzenia wielu funkcji własnych o takiej samej nazwie, różniących się od siebie ilością i typem parametrów.

W poniższym kodzie 1 przygotowałem 3 funkcje własne o jednakowej nazwie MyFunction(). Pierwsza służy do obliczenia sumy 3 liczb typu int. Druga - do obliczenia mnożenia 3 liczb typu double. Trzecia - do obliczenia dzielenia 2 liczb typu double.

Kod 1
#property strict

void OnStart()
  {
   int    a = 1,    b = 2,    c = 3;
   double AA = 8.8, BB = 2.2, CC = 0.5;

   string v = MyFunction(a, b, c);
   Print(v);
  }

//--- 1) funkcja własna do obliczenia sumy
string MyFunction(int f_1, int f_2, int f_3)
  {
   int r = f_1 + f_2 + f_3;
   string result = "Suma: " + IntegerToString(r);
   return(result);
  }

//--- 2) funkcja własna do obliczenia mnożenia
string MyFunction(double f_1, double f_2, double f_3)
  {
   double r = f_1 * f_2 * f_3;
   string result = "Mnożenie: " + DoubleToString(r,5);
   return(result);
  }

//--- 3) funkcja własna do obliczenia dzielenia
string MyFunction(double f_1, double f_2)
  {
   if(f_2 == 0.0)
      return("Nie można dzielić przez 0!");
   double r = f_1 / f_2;
   string result = "Dzielenie: " + DoubleToString(r, 5);
   return(result);
  }

W ciele głównej funkcji skryptu OnStart() utworzono 3 zmienne a, b i c typu int oraz 3 zmienne AA, BB i CC typu double. Następnie utworzono zmienną v typu string (typ tekstowy), której przypisuje się wynik działania funkcji MyFunction() po czym Print(v) wyświetli wartość v w dzienniku logów terminala MT4.

Jeśli zrozumienie działania tych trzech funkcji własnych sprawia ci trudność, proponuję wrócić do poprzednich rozdziałów tej lekcji. Jeśli wszystko w porządku to jedynie w 3-ej funkcji chciałbym skomentować zapis sprawdzenia dzielenia przez zero (kod 2).

Kod 2
if(f_2 == 0.0)
   return("Nie można dzielić przez 0!");

Ze szkoły pamiętamy, że dzielenie przez 0 jest działaniem niedopuszczalnym. W programowaniu doprowadzi to do krytycznego błędu i uruchomiony program się posypie, co z kolei może doprowadzić do nie przewidzianych skutków. Wyobraź sobie, że twoja strategia automatyczna ma otwartą pozycję z bieżącym zyskiem na milion złotych, a tu ba-bach - program się posypał i jesteś w minusie .

Sprawdzenie czy dzielnik nie jest równe 0.0, zabezpiecza program przed krytycznym błędem. Jeśli dla dzielnika funkcja otrzyma zero, to od razu zwróci tekst "Nie można dzielić przez 0!", nie dopuści do dzielenia przez zero i program będzie dalej pracować. Ten tekst pojawi się w logach terminala co będzie ważnym ostrzeżeniem dla programisty, aby poprawić algorytm programu.

Ponieważ wszystkie 3 funkcje maja taką samą nazwę, to którą z nich wybierze program? Otóż w takich sytuacjach program użyje tę z nich, która najlepiej będzie pasować, uwzględniając typy oraz ilość argumentów zapisanych w nagłówku funkcji.


Przykład 1

W nagłówku MyFunction() zapiszmy 3 liczby typu int (kod 3). Niżej przedstawiony kod jest skróconą wersją kodu 1.

Kod 3
#property strict

void OnStart()
  {
   int    a = 1,    b = 2,    c = 3;
   double AA = 8.8, BB = 2.2, CC = 0.5;

   string v = MyFunction(a, b, c);  // do funkcji przekazano 3 liczby typu int
   Print(v);
  }

//--- 1) funkcja własna do obliczenia sumy
string MyFunction(int f_1, int f_2, int f_3)
  {
   int r = f_1 + f_2 + f_3;
   string result = "Suma: " + IntegerToString(r);
   return(result);
  }

//--- 2) funkcja własna do obliczenia mnożenia
//--- 3) funkcja własna do obliczenia dzielenia

W tym przypadku kompilator wybierze pierwszą funkcję do obliczenia sumy, ponieważ widzi on tu 100% zgodność. Ta funkcja też potrzebuje 3 wartości typu int. Po uruchomieniu skryptu w MT4, w logach terminala zobaczymy zapis: „Suma: 6”, co potwierdza słuszność naszego myślenia (1 + 2 + 3 = 6).


Przykład 2

W nagłówku MyFunction() zapiszmy 3 liczby typu double (kod 4). Niżej przedstawiony kod jest skróconą wersją kodu 1.

Kod 4
#property strict

void OnStart()
  {
   int    a = 1,    b = 2,    c = 3;
   double AA = 8.8, BB = 2.2, CC = 0.5;

   string v = MyFunction(AA, BB, CC);  // do funkcji przekazano 3 liczby typu double
   Print(v);
  }

//--- 1) funkcja własna do obliczenia sumy
//--- 2) funkcja własna do obliczenia mnożenia
string MyFunction(double f_1, double f_2, double f_3)
  {
   double r = f_1 * f_2 * f_3;
   string result = "Mnożenie: " + DoubleToString(r, 5);
   return(result);
  }

//--- 3) funkcja własna do obliczenia dzielenia

W tym przypadku kompilator wybierze drugą funkcję do obliczenia mnożenia. Dlaczego nie trzecią, przecież tam parametry też mają typ double? Druga funkcja potrzebuje 3 wartości, a trzecia tylko 2, dlatego 100% zgodność występuje tylko dla drugiej funkcji. Po uruchomieniu takiego skryptu w MT4, w logach terminala zobaczymy zapis: „Mnożenie: 9.68000”, co pasuje do mnożenia (8.8 x 2.2 x 0.5 = 9.68).


Przykład 3

W nagłówku MyFunction() zapiszmy 2 liczby typu double (kod 5). Niżej przedstawiony kod jest skróconą wersją kodu 1.

Kod 5
#property strict

void OnStart()
  {
   int    a = 1,    b = 2,    c = 3;
   double AA = 8.8, BB = 2.2, CC = 0.5;

   string v = MyFunction(BB, CC);  // do funkcji przekazano 2 liczby typu double
   Print(v);
  }

//--- 1) funkcja własna do obliczenia sumy
//--- 2) funkcja własna do obliczenia mnożenia
//--- 3) funkcja własna do obliczenia dzielenia
string MyFunction(double f_1, double f_2)
  {
   if(f_2 == 0.0)
      return("Nie można dzielić przez 0!");
   double r = f_1 / f_2;
   string result = "Dzielenie: " + DoubleToString(r, 5);
   return(result);
  }

W odróżnieniu od przykładu nr 2, teraz 100% zgodność będzie miała miejsce w stosunku do 3-ej funkcji, ponieważ przy wywołaniu MyFunction() wpisaliśmy w jej nagłówku tylko 2 wartości BB i CC. Trzecia z przeciążonych funkcji też potrzebuje tylko 2 wartości typu double. Wynik działania skryptu: „Dzielenie: 4.40000”, tj. 2.2 / 0.5 = 4.4.

W wyżej wymienionych przykładach używałem idealną 100% zgodność z sygnaturą każdej z 3 funkcji. Sygnatura to taki unikalny identyfikator funkcji składający się z jej nazwy, liczby parametrów i ich typów. Zgodnie z sygnaturą kompilator wiedział którą z przeciążonych funkcji wywołać do pracy, kiedy do funkcji przekazywaliśmy albo 3 liczby typu int albo 3 liczby typu double albo 2 liczby typu double. A co się stanie jeśli nie będzie 100% zgodności, jaki wpływ będzie miał sposób przekazywania danych (kopiowanie, wskaźniki), co jeśli argument będzie miał inny typ niż typ przyjmującego parametru? Na te i inne pytania odpowiem w następnym rozdziale.