Yapılar, Sınıflar ve Arayüzler

Yapılar

Yapılar her tipten elemanlar içerebilen kümelerdir (void tipi dışında). Yapıların kullanımı mantıksal olarak ilişkili farklı tip verilerin bir araya getirilmesini sağlar.

Yapı Bildirimi

Yapı veri tipi şu tarif ile belirtilir:

struct yapı_ismi
  {
  elemanların_açıklaması
  };

Yapı ismi bir tanımlayıcı (bir değişken veya fonksiyon ismi) şeklinde kullanılamaz. MQL5 yapı elemanlarının sıralama olmaksızın doğrudan birbirlerini takip ettiği not edilmelidir. Bu gibi bir sıralama C++ dilinde şu yönerge ile derleyiciye yaptırılır:

#pragma pack(1)

Eğer yapı içinde başka bir sıralama yapmak isterseniz doğru ölçüde yardımcı üyeler ve "dolgular" kullanın.

Örnek:

struct trade_settings
  {
   uchar  slippage;     // izin verilebilir slipaj (sapma) değeri 1 bayt
   char   reserved1;    // 1 bayt atla
   short  reserved2;    // 2 bayt atla
   int    reserved4;    // başka bir 4 bayt atlandı. 8 baytlık sınır hizasını temin eder
   double take;         // kar sabitleme fiyatının değerleri
   double stop;         // koruyucu durdurma seviyesinin fiyat değeri
  };

Sıralanmış yapıların bu tanımı, sadece içe aktarılmış dll fonksiyonlarına yapılacak veri aktarımları için gereklidir.

Uyarı: Bu örnek yanlış tasarlanmış veriler sergiler. Önce double tipli take ve stop gibi büyük değişkenlerin, ardından uchar tipli slippage üyesinin bildirilmesi daha iyi olacaktır. Bu durumda, verinin içsel temsili #pragma pack() içinde belirlenen değerden bağımsız olarak her zaman aynı olacaktır.

Eğer bir yapı string tipi ve veya dinamik dizi nesnesi şeklinde değişkenler içeriyorsa, derleyici yapı için gizli bir yapıcı fonksiyon atar. Bu yapıcı tüm string tipli üyeleri sıfırlar ve dinamik dizi nesnelerini düzgün şekilde başlatır.

Basit Yapılar

Dizgileri, nesne işaretçilerini, sınıf nesnelerini ve dinamik dizileri içermeyen yapılar basit yapılardır. Yapı değişkenleri ve bunların dizileri DLL dosyalarından aktarılan fonksiyonlara geçirilebilir.

Basit yapıların kopyalanmasına sadece iki durumda izin verilir:

  • Nesne aynı yapı tipinden ise
  • Nesnelerin köken bağı varsa, yani bir yapı diğerinden türetilmişse.

Örnek olarak, gömülü MqlTick yapısını kullanarak, aynı içeriğe sahip olan özel CustomMqlTick yapısını oluşturalım. Bu durumda derleyici MqlTick nesne değerinin CustomMqlTick tipli nesneye kopyalanmasına izin vermeyecektir. Uygun tipe yapılan doğrudan tip dönüşümü bile derleyici hatası verecektir:

      //--- basit yapılar farklı tiplere dönüştürülemez
      my_tick1=last_tick;               // burada derleyici hata dönüşü yapar
     
      //--- farklı tipli yapıların birbirlerinin tipine dönüştürülmesi de mümkün değildir
      my_tick1=(CustomMqlTick)last_tick;// burada derleyici hata dönüşü yapar

Bu nedenle tek bir seçenek kalır, o da yapı elemanlarının değerlerini tek tek kopyalamaktır. CustomMqlTick ile aynı tipte olan değerlerin kopyalanması mümkündür.

      CustomMqlTick my_tick1,my_tick2;
      //--- CustomMqlTick ile aynı tipteki verilerin kopyalanması şu şekilde mümkündür
      my_tick2=my_tick1;
     
      //--- CustomMqlTick yapısının nesnelerinden bir dizi oluştur ve değerleri buna yaz
      CustomMqlTick arr[2];
      arr[0]=my_tick1;
      arr[1]=my_tick2;

ArrayPrint() fonksiyonu arr[] dizisinin değerini günlükte görüntülemek için çağrılır.

//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- gömülü MqlTick yapısına benzer bir yapı oluştur
   struct CustomMqlTick
     {
      datetime          time;          // Son fiyatın güncelleme zamanı
      double            bid;           // Mevcut Satış fiyatı
      double            ask;           // Mevcut Alış fiyatı
      double            last;          // Son işlemin mevcut fiyatı
      ulong             volume;        // Son işlemin hacmi
      long              time_msc;      // Son işlemin güncelleme zamanı
      uint              flags;         // Tik bayrakları     
     };
   //--- son tik fiyatını al
   MqlTick last_tick;
   CustomMqlTick my_tick1,my_tick2;
//--- MqlTick verilerini CustomMqlTick üzerine kopyalama denemesi
   if(SymbolInfoTick(Symbol(),last_tick))
     {
      //--- ilgisiz basit yapıların kopyalanmasına izin verilmez
      //1. my_tick1=last_tick;               // derleyici burada bir hata dönüşü yapar
     
      //--- ilgisiz yapılar için tip dönüşümüne de izin verilmez
      //2. my_tick1=(CustomMqlTick)last_tick;// derleyici burada bir hata dönüşü yapar
     
      //--- yani, yapı elemanlarını tek tek kopyalamalıyız       
      my_tick1.time=last_tick.time;
      my_tick1.bid=last_tick.bid;
      my_tick1.ask=last_tick.ask;
      my_tick1.volume=last_tick.volume;
      my_tick1.time_msc=last_tick.time_msc;
      my_tick1.flags=last_tick.flags;
     
      //--- CustomMqlTick ile aynı tipteki verilerin kopyalanması şu şekilde mümkündür
      my_tick2=my_tick1;
     
      //--- CustomMqlTick yapısının nesnelerinden bir dizi oluştur ve değerleri buna yaz
      CustomMqlTick arr[2];
      arr[0]=my_tick1;
      arr[1]=my_tick2;
      ArrayPrint(arr);
//--- CustomMqlTick tipli nesneler içeren dizinin değerlerinin görüntülenmesi için örnek
      /*
                       [time]   [bid]   [ask]   [last] [volume]    [time_msc] [flags]
      [0] 2017.05.29 15:04:37 1.11854 1.11863 +0.00000  1450000 1496070277157       2
      [1] 2017.05.29 15:04:37 1.11854 1.11863 +0.00000  1450000 1496070277157       2           
      */
     }
   else
      Print("SymbolInfoTick() başarısız, hata = ",GetLastError());
  }

İkinci örnek aynı soydan iki basit yapının kopyalanmasını göstermektedir. Elimizde Animal (hayvan) yapısından türetilen Cat (kedi) ve Dog (köpek) gibi iki basit yapı olsun. Animal ve Cat nesnelerini (aynı şekilde Animal ve Dog nesnelerini) birbirine kopyalayabiliriz. Ama Cat ve Dog nesneleri aynı soydan (Animal) gelmelerine rağmen birbirlerine kopyalanamaz.

//--- köpekleri tanımlayan yapı
struct Dog: Animal
  {
   bool              hunting;       // vahşi tür
  };
//--- kedileri tanımlayan yapı
struct Cat: Animal
  {
   bool              home;          // evcil tür
  };
//--- türetik yapıları oluştur
   Dog dog;
   Cat cat;
//--- atadan neslinde olana kopyalama yapılabilir (Animal ==> Dog)
   dog=some_animal;
   dog.swim=true;    // köpekler yüzebilir
//--- türetik yapılar birbirlerine kopyalanamaz (Dog != Cat)
   cat=dog;        // derleyici hata dönüşü yapar

Örneğin tam kodu:

//--- hayvanları tanımlayan temel yapı
struct Animal
  {
   int               head;          // baş sayısı
   int               legs;          // bacak sayısı
   int               wings;         // kanat sayısı
   bool              tail;          // kuyruk
   bool              fly;           // uçucu
   bool              swim;          // yüzücü  
   bool              run;           // koşucu
  };
//--- köpekleri tanımlayan yapı
struct Dog: Animal
  {
   bool              hunting;       // vahşi tür
  };
//--- kedileri tanımlayan yapı
struct Cat: Animal
  {
   bool              home;          // evcil tür
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- temel Animal (hayvan) tipli nesneyi tanımla 
   Animal some_animal;
   some_animal.head=1;
   some_animal.legs=4;
   some_animal.wings=0;
   some_animal.tail=true;
   some_animal.fly=false;
   some_animal.swim=false;
   some_animal.run=true;
//--- türetik nesne tiplerini oluştur
   Dog dog;
   Cat cat;
//--- atadan neslinde olana kopyalama yapılabilir (Animal ==> Dog)
   dog=some_animal;
   dog.swim=true;    // köpekler yüzebilir
//--- türetik yapılar birbirlerine kopyalanamaz (Dog != Cat)
   //cat=dog;        // derleyici burada hata dönüşü yapar
//--- ama nesne bileşenleri tek tek kopyalanabilir
   cat.head=dog.head;
   cat.legs=dog.legs;
   cat.wings=dog.wings;
   cat.tail=dog.tail;
   cat.fly=dog.fly;
   cat.swim=false;   // kediler yüzemez
//--- neslinde olandan ataya kopyalama yapılabilir
   Animal elephant;
   elephant=cat;
   elephant.run=false;// filler koşamaz
   elephant.swim=true;// filler yüzer
//--- Bir dizi oluştur
   Animal animals[4];
   animals[0]=some_animal;
   animals[1]=dog;  
   animals[2]=cat;
   animals[3]=elephant;
//--- sonuçları çıktıla
   ArrayPrint(animals);
//--- çalıştırma sonucu
/*
       [head] [legs] [wings] [tail] [fly] [swim] [run]
   [0]      1      4       0   true false  false  true
   [1]      1      4       0   true false   true  true
   [2]      1      4       0   true false  false false
   [3]      1      4       0   true false   true false
*/  
  }

Basit tipli yapıları kopyalamanın bir diğer yolu bileşimdir. Yapı elemanları aynı bileşimin üyeleri olmalıdır – bkz. bileşim örneği.

Yapı Üyelerine Erişim

Yapılara verilen isimler yeni veri tiplerini temsil eder ve yeni değişkenlerin bildirimi bu tipler ile yapılabilir. Yapılar bir proje içinde yalnızca bir defa bildirilebilir. Yapı üyelerine nokta işlemi (.) kullanılarak erişilebilir.

Örnek:

struct trade_settings
  {
   double take;         // kar sabitleme değerleri
   double stop;         // koruyucu durdurma fiyatı değeri
   uchar  slippage;     // kabul edilebilir slipaj
  };
//--- trade_settings tipinde bir değişken oluştur ve başlat
trade_settings my_set={0.0,0.0,5};  
if (input_TP>0) my_set.take=input_TP;

yapı ve sınıf alanlarını hizalamak için 'pack' #

Özel pack özelliği, yapı veya sınıf alanlarının hizalanmasını sağlar.

 pack([n])

burada n, şu değerlerden biridir: 1, 2, 4, 8 ya da 16. n değeri bulunmayadabilir.

Örnek:

   struct pack(sizeof(long)) MyStruct
     {
      // yapı üyeleri 8 bayt sınırına hizalanacak
     };
or
   struct MyStruct pack(sizeof(long))
     {
      // yapı üyeleri 8 bayt sınırına hizalanacak
     };

Yapılar için varsayılan olarak 'pack(1)' uygulanır. Bu, yapı elemanlarının birbiri ardına hafızaya yerleştirildiği ve yapı büyüklüğünün üyelerinin büyüklüğünün toplamına eşit olduğu anlamına gelir.

Örnek:

//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- hizasız basit yapı
   struct Simple_Structure
     {
      char              c; // sizeof(char)=1
      short             s; // sizeof(short)=2
      int               i; // sizeof(int)=4
      double            d; // sizeof(double)=8
     };
   //--- basit bir yapı örneği bildir   
   Simple_Structure s;  
//--- her yapı üyesinin boyutunu göster  
   Print("sizeof(s.c)=",sizeof(s.c));
   Print("sizeof(s.s)=",sizeof(s.s));
   Print("sizeof(s.i)=",sizeof(s.i));
   Print("sizeof(s.d)=",sizeof(s.d));
//--- POD yapısının büyüklüğünün, üyelerinin büyüklüğünün toplamına eşit olduğundan emin ol
   Print("sizeof(simple_structure)=",sizeof(simple_structure));
/*
  Result:
   sizeof(s.c)=1
   sizeof(s.s)=2
   sizeof(s.i)=4
   sizeof(s.d)=8
   sizeof(simple_structure)=15 
*/    
  }

Bu tür bir hizalamanın uygulandığı üçüncü taraf kütüphaneleriyle (*.DLL) veri alışverişinde bulunurken yapı alanlarının hizalanması gerekebilir.

Hizalamanın nasıl çalıştığını göstermek için bazı örnekler kullanalım. Dört üyeden oluşan hizasız bir yapı uygulayacağız.

//--- hizasız basit yapı
   struct Simple_Structure pack() // boyut belirtilmemiş, 1 bayt sınırına hizalama ayarlanacak
     {
      char              c; // sizeof(char)=1
      short             s; // sizeof(short)=2
      int               i; // sizeof(int)=4
      double            d; // sizeof(double)=8
     };
//--- basit bir yapı örneği bildir  
   Simple_Structure s;

Yapı alanları şu durumlara göre birbiri ardına hafızaya yerleştirilmelidir: bildiri sırası ve tip boyutu. Yapı boyutu 15'tir, dizilerdeki yapı alanlarına olan öteleme tanımsızdır.

simple_structure_alignment

Şimdi aynı yapıyı 4 bayt olarak hizalayarak bildirin ve kodu çalıştırın.

//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 4 baytlık hizalama ile basit yapı
   struct Simple_Structure pack(4)
     {
      char              c; // sizeof(char)=1
      short             s; // sizeof(short)=2
      int               i; // sizeof(int)=4
      double            d; // sizeof(double)=8
     };
   //--- basit bir yapı örneği bildir   
   Simple_Structure s;  
//--- her yapı üyesinin boyutunu göster  
   Print("sizeof(s.c)=",sizeof(s.c));
   Print("sizeof(s.s)=",sizeof(s.s));
   Print("sizeof(s.i)=",sizeof(s.i));
   Print("sizeof(s.d)=",sizeof(s.d));
//--- şimdi, POD yapısının boyutunun, üyelerinin boyutlarının toplamına eşit olmadığından emin olun
   Print("sizeof(simple_structure)=",sizeof(simple_structure));
/*
  Result:
   sizeof(s.c)=1
   sizeof(s.s)=2
   sizeof(s.i)=4
   sizeof(s.d)=8
   sizeof(simple_structure)=16 // yapı boyutu değişti
*/    
  }

Yapı boyutu değişti, böylece 4 bayt ve daha fazla olan tüm üyeler, yapının başlangıcından itibaren 4 baytın katları kadar olacak şekilde ötelemeye sahip oldu. Daha küçük üyeler kendi boyut sınırlarına göre hizalanmalıdır (örneğin, 'short' için 2). Görünüşü şu şekildedir (eklenen bayt gri renkte gösterilir).

simple_structure_alignment_pack

Bu durumda, s.c üyesinden sonra 1 bayt eklenmiştir, böylece s.s (sizeof(short)==2) alanı 2 bayt sınırına sahip olmuştur ('short' tipi için hizalama).

Dizideki yapının başlangıcına olan öteleme da 4-bayt sınırına hizalanır, diğer bir deyişle Simple_Structure arr[] için a[0], a[1] ve a[n] elemanlarının adresleri 4 baytın katları olacaktır.

4 baytlık hizalamaya ancak farklı üye sırasına sahip benzer tiplerden oluşan iki yapı daha ele alalım. İlk yapıda, üyeler artan tip boyutu sırasında bulunurlar.

//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 4 bayt sınırına göre düzenlenmiş basit yapı
   struct CharShortInt pack(4)
     {
      char              c; // sizeof(char)=1
      short             s; // sizeof(short)=2
      int               i; // sizeof(double)=4
     };
//--- basit bir yapı örneği bildir  
   CharShortInt ch_sh_in;
//--- her yapı üyesinin boyutunu göster  
   Print("sizeof(ch_sh_in.c)=",sizeof(ch_sh_in.c));
   Print("sizeof(ch_sh_in.s)=",sizeof(ch_sh_in.s));
   Print("sizeof(ch_sh_in.i)=",sizeof(ch_sh_in.i));
 
//--- POD yapısının büyüklüğünün, üyelerinin büyüklüğünün toplamına eşit olduğundan emin ol
   Print("sizeof(CharShortInt)=",sizeof(CharShortInt));
/*
  Result:
   sizeof(ch_sh_in.c)=1
   sizeof(ch_sh_in.s)=2
   sizeof(ch_sh_in.i)=4
   sizeof(CharShortInt)=8
*/   
  }

Gördüğümüz üzere, yapı boyutu 8'dir ve iki adet 4 baytlık bloktan oluşur. İlk blok 'char' ve 'short' tipindeki alanları, ikincisi ise 'int' tipindeki alanı içerir.

charshortint

Şimdi, ilk yapıyı, 'short' tipi üyesinin sona taşınmasıyla sadece alan sırasında farklılık gösteren ikinci yapıya dönüştürelim.

//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 4 bayt sınırına göre düzenlenmiş basit yapı
   struct CharIntShort pack(4)
     {
      char              c; // sizeof(char)=1
      int               i; // sizeof(double)=4
      short             s; // sizeof(short)=2
     };
//--- basit bir yapı örneği bildir  
   CharIntShort ch_in_sh;
//--- her yapı üyesinin boyutunu göster  
   Print("sizeof(ch_in_sh.c)=",sizeof(ch_in_sh.c));
   Print("sizeof(ch_in_sh.i)=",sizeof(ch_in_sh.i));
   Print("sizeof(ch_in_sh.s)=",sizeof(ch_in_sh.s));
//--- POD yapısının büyüklüğünün, üyelerinin büyüklüğünün toplamına eşit olduğundan emin ol
   Print("sizeof(CharIntShort)=",sizeof(CharIntShort));
/*
  Result:
   sizeof(ch_in_sh.c)=1
   sizeof(ch_in_sh.i)=4
   sizeof(ch_in_sh.s)=2
   sizeof(CharIntShort)=12
*/   
  }

Yapı içeriği değişmemiş olsa da, üye sırasını değiştirmek yapının boyutunu arttırdı.

charintshort

Devralınırken hizalama da göz önünde bulundurulmalıdır. Bunu tek 'char' tipi üyesine sahip basit Parent yapısını kullanarak gösterelim. Hizasız yapı boyutu 1'dir.

   struct Parent
     {
      char              c;    // sizeof(char)=1
     };

'short' (sizeof(short)=2) tipi üyesini içeren Children alt sınıfını oluşturalım.

   struct Children pack(2) : Parent
     {
      short             s;   // sizeof(short)=2
     };

Sonuç olarak, 2 bayta hizalama yapılırken, üyelerinin boyutu 3 olmasına rağmen yapı boyutu 4'e eşittir. Bu örnekte, Parent sınıfına 2 bayt ayrılacaktır, böylece alt sınıfın 'short' alanına erişim 2 bayt ile hizalanmış olacaktır.

Bir MQL5 uygulaması, dosyalar veya akışlar düzeyinde yazarak/okuyarak üçüncü taraf verileriyle etkileşime girerse, yapı üyeleri için belleğin nasıl ayrıldığının bilgisi gereklidir.

Standart Kütüphanenin MQL5\Include\WinAPI dizini WinAPI fonksiyonlarıyla çalışmak için fonksiyonlar içerir. Bu fonksiyonlar, WinAPI ile çalışmak için gereken durumlarda, belirtilen bir hizalama ile yapıları uygular.  

offsetof doğrudan pack özniteliği ile ilgili özel bir komuttur. Yapının başından bir üye ötelemesi elde etmemizi sağlar.

//--- Children tipi değişkeni bildir
   Children child;  
//--- yapının başlangıcından ötelemeleri (ofsetleri) sapta
   Print("offsetof(Children,c)=",offsetof(Children,c));
   Print("offsetof(Children,s)=",offsetof(Children,s));  
/*
  Result:
   offsetof(Children,c)=0
   offsetof(Children,s)=2
*/   

'final' Şekillendiricisi #

Yapıların bildirimi sırasında 'final' şekillendiricisinin kullanımı, kalıtımı engeller. Yapı üzerinde daha fazla değişikliğe ihtiyaç duyulmuyorsa veya güvenlik gerekçeleriyle izin verilmiyoyarsa, yapıyı bildirirken 'final' şekillendiricisini kullanabilirsiniz. Bu şekilde yapının tüm elemanlarının –kapalı yolla– son şekillerini aldığı varsayılır.

struct settings final
  {
  //--- Yapı gövdesi
  };
 
struct trade_settings : public settings
  {
  //--- yapı gövdesi
  };

Yukarıdaki örnekte de gösterildiği gibi, 'final' şekillendiricisi ile bildirilmiş bir yapıdan kalıtım yapılmak istendiğinde, derleyici hata dönüşü yapacaktır:

cannot inherit from 'settings' as it has been declared as 'final'
see declaration of 'settings'

Sınıflar #

Sınıflar aşağıda belirtilen maddeler açısından yapılardan ayrılır:

  • class anahtar sözcüğü bildirimde kullanılır;
  • Aksi belirtilmemişse, varsayılan olarak tüm sınıf üyeleri private erişim belirtecine sahiptir. Aksi belirtilmemişse, yapıların veri üyeleri 'public' erişim belirtecine sahiptir;
  • Sınıf içinde bildirilen bir sanal fonksiyon olmasa bile sınıf üyeleri her zaman bir sanal fonksiyonlar tablosuna sahiptir. Yapılar sanal fonksiyonlara sahip olamaz;
  • new operatörü sınıf nesnelerine uygulanabilir ama yapılara uygulanamaz;
  • sınıflar, diğer sınıflardan kalıtsal yolla oluşturulabilir. Aynı şekilde yapılar da sadece yapılardan kalıtsal olarak oluşturulabilir.

Sınıflar ve yapılar açık yapıcı ve yıkıcı fonksiyonlara sahip olabilirler. Eğer yapı fonksiyon açık şekilde tanımlanmışsa, yapı veya sınıf değişkeninin başlatma sırası kullanılarak başlatılması imkansızdır.

Örnek:

struct trade_settings
  {
   double take;         // kar sabitleme değerleri
   double stop;         // koruyucu durdurma fiyatı değeri
   uchar  slippage;     // kabul edilebilir slipaj
   //--- Yapıcı
          trade_settings() { take=0.0; stop=0.0; slippage=5; }
   //--- Yıkıcı
         ~trade_settings() { Print("Bu, son"); } 
  };
//--- Derleyici başlatmanın imkansız olduğunu belirten bir hata mesajı oluşturacaktır
trade_settings my_set={0.0,0.0,5};  

Yapıcı ve Yıkıcı Fonksiyonlar

Yapıcı, bir yapı veya sınıf nesnesi oluşturulduğunda otomatik olarak çağrılan özel bir fonksiyondur. Genellikle sınıf üyelerini başlatmak için kullanılır. Aynı uygulamalar yapılar için de geçerli olacağından, bundan sonra aksi belirtilmedikçe sadece sınıflar hakkında konuşacağız. Yapıcının ismi sınıf ismi ile örtüşmelidir. Yapıcının bir dönüş tipi yoktur (void tipini belirtebilirsiniz).

Tanımlanmış sınıf üyeleri – dizeler, dinamik diziler ve başlatma gerektiren nesneler – bir yapıcı olup olmadığına bakılmaksızın her durumda başlatılır.

Her sınıf parametre sayısı açısından veya başlatma listesi açısından farklı olan birden fazla yapıcı fonksiyon içerebilir. Parametrelerinin belirtilmesi gereken yapıcılara parametrik yapıcı denir.

Parametresiz olarak bildirilen yapıcılar ön-tanımlı yapıcılardır. Eğer sınıf içinde hiç yapıcı bildirilmemişse derleyici otomatik olarak bir ön-tanımlı yapıcı oluşturacaktır.

//+------------------------------------------------------------------+
//| Tarihle çalışmak için bir sınıf                                  |
//+------------------------------------------------------------------+
class MyDateClass
  {
private:
   int               m_year;          // Yıl
   int               m_month;         // Ay
   int               m_day;           // Ayın günü
   int               m_hour;          // Günün saati
   int               m_minute;        // Dakikalar
   int               m_second;        // Saniyeler
public:
   //--- Ön tanımlı yapıcı
                     MyDateClass(void);
   //--- Parametrik yapıcı
                     MyDateClass(int h,int m,int s);
  };

 

Yapıcı fonksiyonlar sınıf tarifinde bildirilebilir ve gövdeleri daha sonra tanımlanabilir. Örneğin MyDateClass'ın iki yapıcısı, şu yolla tanımlanabilir:

//+------------------------------------------------------------------+
//| Ön tanımlı yapıcı                                                |
//+------------------------------------------------------------------+
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__);
  }
//+------------------------------------------------------------------+
//| Parametrik yapıcı                                                |
//+------------------------------------------------------------------+
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__);
  }

Ön-tanımlı yapıcının içindeki tüm sınıf üyeleri TimeCurrent() fonksiyonu ile doldurulur. Parametrik yapıcı içindeyse sadece saat verileri kullanılır. Diğer sınıf üyeleri (m_year, m_month ve m_day) otomatik olarak mevcut tarihle başlatılır.

Ön tanımlı yapıcı kendi sınıfı içindeki bir nesne dizisi başlatılırken özel bir amaca sahiptir. Tüm parametreleri ön-tanımlı değerler alan yapılar ön-tanımlı yapı değildir. Bir örnek:

//+------------------------------------------------------------------+
//| Ön-tanımlı yapıcıya sahip bir sınıf                              |
//+------------------------------------------------------------------+
class CFoo
  {
   datetime          m_call_time;     // Son nesne çağrısının zamanı
public:
   //--- Ön tanımlı bir parametreye sahip olan yapıcı, bir ön tanımlı yapıcı değildir
                     CFoo(const datetime t=0){m_call_time=t;};
   //--- Kopya yapıcı
                     CFoo(const CFoo &foo){m_call_time=foo.m_call_time;};
 
   string ToString(){return(TimeToString(m_call_time,TIME_DATE|TIME_SECONDS));};
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
// CFoo foo; // Bu varyant kullanılamaz - bir ön tanımlı yapıcı ayarlanmamış
//--- CFoo nesnesini oluşturmak için seçenekler
   CFoo foo1(TimeCurrent());     // Bir parametrik yapıcının açık çağrısı
   CFoo foo2();                  // Ön tanımlı parametreye sahip bir parametrik yapıcının açık çağrısı
   CFoo foo3=D'2009.09.09';      // Bir parametrik yapıcının gizli çağrısı
   CFoo foo40(foo1);             // Kopya yapıcının açık çağrısı 
   CFoo foo41=foo1;              // Kopya yapıcının kapalı çağrısı
   CFoo foo5;                    // Bir ön tanımlı yapıcının açık çağrısı ( eğer hiç ön tanımlı yapıcı yok sa,
                                 // o zaman ön tanımlı değere sahip bir parametrik yapıcı çağrılır)
//--- CFoo işaretçilerinin alınması için muhtemel seçenekler
   CFoo *pfoo6=new CFoo();       // Nesnenin dinamik olarak oluşturulması ve buna atanan bir işaretçinin alınması
   CFoo *pfoo7=new CFoo(TimeCurrent());// Dinamik nesne oluşturmak için bir diğer seçenek
   CFoo *pfoo8=GetPointer(foo1); // şimdi pfoo8, foo1 nesnesini işaretliyor
   CFoo *pfoo9=pfoo7;            // pfoo9 ve pfoo7 aynı nesneye işaret ediyor
   // CFoo foo_array[3];         // Bu seçenek kullanılamaz - bir ön tanımlı yapıcı belirtilmemiş
//--- m_call_time değerini göster
   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());
//--- Dinamik olarak oluşturulmuş dizileri sil
   delete pfoo6;
   delete pfoo7;
   //delete pfoo8;  // pfoo8, otomatik olarak foo1 oluşturulmuş nesnesini gösterdiğinden, bunu açıkça silmek zorunda değilsiniz
   //delete pfoo9;  // pfoo7 ile aynı nesneye işaret ettiğinden pfoo9'u açıkça silmek zorunda değilsiniz.
  }

Bu dizeleri yorumsuz bırakırsanız

  //CFoo foo_array[3];     // Bu varyant kullanılamaz - ön tanımlı bir yapıcı ayarlanmamış

veya

  //CFoo foo_dyn_array[];  // Bu varyant kullanılamaz - ön tanımlı bir yapıcı ayarlanmamış

Bu durumda derleyici, "ön tanımlı yapıcı ayarlanmamış" şeklinde bir hata verecektir.

Eğer bir sınıf kullanıcı tanımlı bir yapıcıya sahipse derleyici tarafından ön tanımlı bir yapıcı oluşturulmaz. Bu demektir ki, eğer sınıfın içinde bir ön tanımlı yapıcı değil, bir parametrik yapıcı bildirilmişse bu sınıfın nesnelerinin dizilerini bildiremezsiniz. Derleyici bu betik için bir hata dönüşü yapacaktır:

//+------------------------------------------------------------------+
//| Ön tanımlı yapıcısı olmayan bir sınıf                            |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
                     CFoo(string name) { m_name=name;}
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Derleme sırasında "ön tanımlı yapıcı tanımlanmamış" hatasını al
   CFoo badFoo[5];
  }

Bu örnekte, CFoo sınıfı bir parametrik yapıcıya sahiptir – böyle durumlarda derleyici, derleme esnasında otomatik olarak bir ön tanımlı yapıcı oluşturmaz. Aynı zamanda, bir nesne dizisi bildirdiğinizde tüm nesnelerin otomatik olarak oluşturulması ve başlatılması gerektiği varsayılır. Bir nesnenin otomatik olarak başlatılması sırasında, bir ön tanımlı yapıcı çağrılması gerekir ama ön tanımlı yapıcı açıkça bildirilmedikçe veya derleyici tarafından otomatik olarak oluşturulmadıkça böyle bir nesneyi oluşturmak imkansızdır. Bu yüzden derleyici, derleme sırasında bir hata oluşturur.

Bir nesneyi, bir yapı aracılığıyla başlatmak için özel bir sözdizim mevcuttur. Bir sınıf veya yapı için, yapı başlatıcıları (başlatma için özel yapılar) başlatma listesinde belirlenebilir.

Bir başlatma listesi, yapıcının parametre listesi sonundaki iki noktanın ardından gelen, virgül ile ayrılmış bir başlatıcılar listesidir; gövdeden önce gelir (açılış parantezinin öncesinde). Birkaç gereksinimi vardır:

Burada, sınıf üyelerinin başlatılması için birkaç yapıcıya örnek verilmiştir.

//+------------------------------------------------------------------+
//| Bir karakterin ismini saklaması için bir sınıf                   |
//+------------------------------------------------------------------+
class CPerson
  {
   string            m_first_name;     // İlk isim 
   string            m_second_name;    // Soy isim
public:
   //--- Boş bir ön tanımlı yapıcı
                     CPerson() {Print(__FUNCTION__);};
   //--- Bir parametrik yapıcı
                     CPerson(string full_name);
   //--- Başlatma listeli bir yapıcı
                     CPerson(string surname,string name): m_second_name(surname), m_first_name(name) {};
   void PrintName(){PrintFormat("İsim=%s Soyisim=%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);
     }
  }
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Bir hata al "ön tanımlı yapıcı tanımlanmamış"
   CPerson people[5];
   CPerson Tom="Tom Sawyer";                       // Tom Sawyer
   CPerson Huck("Huckleberry","Finn");             // Huckleberry Finn
   CPerson *Pooh = new CPerson("Winnie","Pooh");  // Winnie the Pooh
   //--- Çıktı değerleri
   Tom.PrintName();
   Huck.PrintName();
   Pooh.PrintName();
   
   //--- Dinamik olarak oluşturulmuş nesneleri sil
   delete Pooh;
  }

Bu durumda CPerson sınıfının üç yapıcısı vardır:

  1. Bu sınıfın nesnelerinden bir dizi oluşturmayı sağlayan açık bir ön tanımlı yapıcı;
  2. Bütün ismi bir parametre olarak alan ve sonra bunu, bulunan alanlara göre ilk isim ve soy isim olarak bölen tek parametreli bir yapıcı;
  3. Bir başlatma listesi içeren iki parametreli bir yapıcı. Başlatıcılar - m_second_name(surname) and m_first_name(name).

Liste ile başlatma işleminin atamanın yerini değiştirdiğini not edin. Tekil üyeler şu şekilde başlatılmalıdır:

 class_member (ifadelerden oluşan bir liste)

Başlatma listesinde üyeler herhangi şekilde sıralanabilir ama bütün sınıf üyeleri duyuruldukları sıra ile başlatılır. Bu demektir ki, üçüncü yapıcıda ilk olarak m_first_name üyesi başlatılır çünkü ilk o bildirilmiştir ve ancak ondan sonra m_second_name başlatılır. Bazı üyelerin başlatılmasının diğer sınıf üyelerinin değerlerine bağlı olduğu durumlarda, bu hesaba katılmalıdır.

Eğer temel sınıf içinde bir ön tanımlı yapıcı bildirilmişse ve parametreli olarak bir veya birden fazla yapıcı bildirilmişse, her zaman başlatma listesindeki temel sınıf yapıcılarından birini çağırmalısınız. Bunlar, listenin sıradan üyeleri gibi virgülle geçer ve başlatma listesinin nerede konumlandığına bakılmaksızın, nesnenin başlatılması sırasında ilk olarak çağrılır.

//+------------------------------------------------------------------+
//| Temel sınıf                                                      |
//+------------------------------------------------------------------+
class CFoo
  {
   string            m_name;
public:
   //--- Başlatma listeli bir yapıcı
                     CFoo(string name) : m_name(name) { Print(m_name);}
  };
//+------------------------------------------------------------------+
//| CFoo sınıfından türetilen sınıf                                  |
//+------------------------------------------------------------------+
class CBar : CFoo
  {
   CFoo              m_member;      // Bir sınıf üyesi ebeveynin nesnesidir
public:
   //--- Başlatma listesindeki bir ön tanımlı yapıcı ebeveyn yapıcısını çağırır
                     CBar(): m_member(_Symbol), CFoo("CBAR") {Print(__FUNCTION__);}
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
   CBar bar;
  }

Bu örnekte, bar nesnesini oluştururken bir ön tanımlı yapıcı CBar() çağrılacaktır. Bunun için önce ebeveyn CFoo için bir yapıcı çağrılır ve ardından m_member sınıf üyesi gelir.

Yıkıcı, bir sınıf nesnesi yok edildiğinde çağrılan özel bir fonksiyondur. Yıkıcının ismi bir sınıf ismi gibi tilde işareti ile (~) yazılır. Sonlandırma gerektiren dizgiler, dinamik diziler ve nesneler, yıkıcının varlığından bağımsız olarak sonlandırılır. Eğer bir yıkıcı varsa, o zaman bu eylemler yıkıcı çağrıldıktan sonra gerçekleştirilecektir.

Yıkıcılar, virtual anahtar sözcüğü ile bildirilmiş olup olmadıklarına bakılmaksızın daima sanaldır.

Sınıf Yöntemlerini Tanımlamak

Sınıf fonksiyon-yöntemleri sınıf içinde veya dışında tanımlanabilir. Eğer yöntem sınıf içinde tanımlanmışsa, gövde hemen yöntemin ardından gelir.

Örnek:

class CTetrisShape
  {
protected:
   int               m_type;
   int               m_xpos;
   int               m_ypos;
   int               m_xsize;
   int               m_ysize;
   int               m_prev_turn;
   int               m_turn;
   int               m_right_border;
public:
   void              CTetrisShape();
   void              SetRightBorder(int border) { m_right_border=border; }
   void              SetYPos(int ypos)          { m_ypos=ypos;           }
   void              SetXPos(int xpos)          { m_xpos=xpos;           }
   int               GetYPos()                  { return(m_ypos);        }
   int               GetXPos()                  { return(m_xpos);        }
   int               GetYSize()                 { return(m_ysize);       }
   int               GetXSize()                 { return(m_xsize);       }
   int               GetType()                  { return(m_type);        }
   void              Left()                     { m_xpos-=SHAPE_SIZE;    }
   void              Right()                    { m_xpos+=SHAPE_SIZE;    }
   void              Rotate()                   { m_prev_turn=m_turn; if(++m_turn>3) m_turn=0; }
   virtual void      Draw()                     { return;                }
   virtual bool      CheckDown(int& pad_array[]);
   virtual bool      CheckLeft(int& side_row[]);
   virtual bool      CheckRight(int& side_row[]);
  }; 

SetRightBorder(int border)'dan, Draw()'a kadar fonksiyonlar, doğrudan CTetrisShape sınıfının içinde tanımlanırlar.

CTetrisShape() yapıcısı ve CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) ve CheckRight(int& side_row[]) yöntemleri sadece sınıf içinde tanımlanırlar ama henüz tanımlanmamış durumdadırlar. Bu fonksiyonların tanımları daha ilerde kodda yer alacaktır. Yöntemi sınıf dışında tanımlamak için kapsam çözünürlük operatörü kullanılır (sınıf ismi kapsam olarak kullanılır).

Örnek:

//+------------------------------------------------------------------+
//| Temel sınıfın yapıcısı                                           |
//+------------------------------------------------------------------+
void CTetrisShape::CTetrisShape()
  {
   m_type=0;
   m_ypos=0;
   m_xpos=0;
   m_xsize=SHAPE_SIZE;
   m_ysize=SHAPE_SIZE;
   m_prev_turn=0;
   m_turn=0;
   m_right_border=0;
  }
//+------------------------------------------------------------------+
//| Aşağı hareket kabiliyetinin kontrol edilmesi (çubuk ve küp için) |
//+------------------------------------------------------------------+
bool CTetrisShape::CheckDown(int& pad_array[])
  {
   int i,xsize=m_xsize/SHAPE_SIZE;
//---
   for(i=0; i<xsize; i++)
     {
      if(m_ypos+m_ysize>=pad_array[i]) return(false);
     }
//---
   return(true);
  }

Public, Protected ve Private Erişim Şekillendiricileri

Yeni bir sınıf geliştirirken üyelere dışarıdan erişimin sınırlanması tavsiye edilir. Bunun için private veya protected anahtar sözcükleri kullanılır. İlk durumda gizli verilere sadece aynı sınıfın içindeki yöntemler erişebilir. Eğer protected anahtar sözcüğü kullanılmışsa, gizli verilere kalıtımsal türetik sınıfların yöntemleri de erişebilecektir. Aynı yöntem sınıf yöntemlerine yapılacak erişimi engellemek için de kullanılabilir.

Bir sınıfın üyelerine ve/veya yöntemlerine erişimi tamamen açmanız gerekiyorsa public anahtar sözcüğünü kullanın.

Örnek:

class CTetrisField
  {
private:
   int               m_score;                            // Skor
   int               m_ypos;                             // Şekillerin mevcut konumları
   int               m_field[FIELD_HEIGHT][FIELD_WIDTH]; // Kuyu matrisi
   int               m_rows[FIELD_HEIGHT];               // Kuyu satırlarının numaralanması
   int               m_last_row;                         // Son boş satır
   CTetrisShape     *m_shape;                            // Tetris şekli
   bool              m_bover;                            // Oyun bitti
public:
   void              CTetrisField() { m_shape=NULL; m_bover=false; }
   void              Init();
   void              Deinit();
   void              Down();
   void              Left();
   void              Right();
   void              Rotate();
   void              Drop();
private:
   void              NewShape();
   void              CheckAndDeleteRows();
   void              LabelOver();
  }; 

public: anahtar sözcüğünden sonra (ve bir sonraki erişim belirtecinden önce) bildirilen her sınıf üyesi ve yöntemi, programın sınıfa yapacağı her referansta kullanılabilir olacaktır. Bunlar örnek içinde şu üyelere karşılık gelir: CTetrisField(), Init(), Deinit(), Down(), Left(), Right(), Rotate() ve Drop() fonksiyonları.

private: anahtar sözcüğünden sonra (ve bir sonraki erişim belirtecinden önce) bildirilen her sınıf üyesi ve yöntemi, sadece bu sınıfın üyeleri-fonksiyonları için kullanılabilir. Erişim belirteçleri daima iki nota (:) ile sonlandırılır ve sınıf tanımında defalarca görülebilir.

protected: erişim belirteci sonrasında bildirilen tüm sınıf üyeleri (bir sonraki erişim belirtecine kadar), yalnızca bu sınıfın üye fonksiyonları ve bu sınıfın nesillerinin üye fonksiyonları için kullanılabilir. Dışarıdan private ve protected belirteçleri olan üyelere erişmeye çalışırken derleme aşamasında hata alınacaktır. Örnek:

class A
  {
protected:
   //--- kopya operatörü yalnızca A sınıfı ve nesillerinde bulunur
   void operator=(const A &)
     {
     }
  };
class B
  {
   //--- A sınıfı nesnesi bildirildi
   A                 a;
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
   //--- iki B tipi değişken bildir
   B b1b2;
   //--- bir nesneyi diğerine kopyalamaya çalış
   b2=b1;
  }

Bu kodu derlerken, bir hata mesajı alınacaktır - uzak kopya operatörü çağrısı:

attempting to reference deleted function 'void B::operator=(const B&)'   trash3.mq5   32   6

Aşağıdaki ikinci dizge daha ayrıntılı bir açıklama sağlar - B sınıfındaki kopya operatörü, A sınıfının kullanılamayan kopya operatörü çağrıldığından açıkça silinmiştir:

   function 'void B::operator=(const B&)' was implicitly deleted because it invokes inaccessible function 'void A::operator=(const A&)' 

Temel sınıfın üyelerine yapılacak erişim, türetilmiş sınıflardaki kalıtım sırasında yeniden tanımlanabilir.

'delete' belirteci

delete belirteci, kullanılamayan sınıf üye fonksiyonlarını işaretler. Bu, eğer program açıkça veya dolaylı olarak böyle bir fonksiyona atıfta bulunuyorsa, derleme aşamasında çoktan hata alınacağı anlamına gelir. Örneğin, bu belirteç, bir alt sınıfta üst yöntemlerin kullanılamamasını sağlar. Fonksiyonu, eğer üst sınıfın özel alanında bildirirsek (private bölümündeki bildirimler), aynı sonuç elde edilebilir. Burada, delete kullanmak, kodu nesiller seviyesinde daha okunabilir ve yönetilebilir hale getirir.

class A
  {
public:
                     A(void) {value=5;};
   double            GetValue(void) {return(value);}
private:
   double            value;
  };
class Bpublic A
  {
   double            GetValue(void)=delete;
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- A tipi değişkeni bildir
   A a;
   Print("a.GetValue()="a.GetValue());
//--- B tipi değişkenden değer elde et
   B b;
   Print("b.GetValue()="b.GetValue()); // derleyici bu dizgede bir hata göstermektedir
  }

Derleyici mesajı:

attempting to reference deleted function 'double B::GetValue()'   
   function 'double B::GetValue()' was explicitly deleted here   

'delete' belirteci, otomatik dönüşümün veya kopya yapıcısının devre dışı bırakılmasına olanak tanır (aksi takdirde private bölümünde de gizlenmesi gerekirdi).  Örnek:

class A
  {
public:
   void              SetValue(double v) {value=v;}
   //--- int tipi cağrıyı devre dışı bırak
   void              SetValue(int) = delete;
   //--- kopya operatörünü devre dışı bırak
   void              operator=(const A&) = delete;
private:
   double            value;
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- iki A tipi değişken bildir
   A a1a2;
   a1.SetValue(3);      // hata!
   a1.SetValue(3.14);   // TAMAM
   a2=a1;               // hata!
  }

Derleme sırasında şu hata mesajlarını almaktayız:

attempting to reference deleted function 'void A::SetValue(int)' 
   function 'void A::SetValue(int)' was explicitly deleted here 
attempting to reference deleted function 'void A::operator=(const A&)'  
   function 'void A::operator=(const A&)' was explicitly deleted here  

'final' Şekillendiricisi #

Sınıfın bildirimi sırasında 'final' şekillendiricisinin kullanımı, kalıtımı engeller. Sınıf üzerinde daha fazla değişikliğe ihtiyaç duyulmuyorsa veya güvenlik gerekçeleriyle izin verilmiyorsa, sınıf bildiriminde 'final' şekillendiricisini kullanın. Bu şekilde sınıfın tüm elemanlarının –kapalı yolla– son şekillerini aldığı varsayılır.

class CFoo final
  {
  //--- Sınıf gövdesi
  };
 
class CBar : public CFoo
  {
  //--- Sınıf gövdesi
  };

Yukarıdaki örnekte de gösterildiği gibi, 'final' şekillendiricisi ile bildirilmiş bir sınıftan kalıtım yapılmak istendiğinde derleyici hata dönüşü yapacaktır:

cannot inherit from 'CFoo' as it has been declared as 'final'
see declaration of 'CFoo'

Birleşimler (union) #

Birleşim, aynı bellek alanını paylaşan birkaç değişkenden oluşan özel bir veri türüdür. Bu nedenle, birleşim aynı bit dizisini iki (veya daha fazla) farklı yolla yorumlama yeteneği sağlar. Birleşim bildirimi, yapı bildirimine benzer ve union anahtar kelimesiyle başlar.

union LongDouble
{
  long   long_value;
  double double_value;
};

Yapının aksine, çeşitli birleşim üyeleri aynı hafıza alanına aittir. Bu örnekte, LongDouble birleşimi, aynı bellek alanını paylaşan long ve double tipleriyle bildirilmektedir. long_value ve double_value değişkenleri çakıştığından (bellekte), birleşim deposunu aynı anda bir long tamsayı değeri ve bir double gerçek değeri yapmanın mümkün olmadığını unutmayın (bir yapıdan farklı olarak). Diğer taraftan, bir MQL5 programı, birleşimde bulunan verileri herhangi bir zamanda bir tamsayı (long) veya gerçek (double) değer olarak işleyebilir. Bu nedenle, birleşim aynı veri sırasını temsil etmek için iki (veya daha fazla) seçenek almaya izin verir.

Birleşim bildirimi sırasında, derleyici otomatik olarak değişken birleşimi içinde en büyük tipi (hacme göre) depolamak için yeterli bellek alanını ayırır. Aynı sözdizimi, birleşim elemanına erişim için yapılarda olduğu gibi kullanılır - nokta operatörü.

union LongDouble
{
  long   long_value;
  double double_value;
};
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   LongDouble lb;
//--- geçersiz -nan(ind) numarasını alın ve görüntüleyin
   lb.double_value=MathArcsin(2.0);
   printf("1.  double=%f                integer=%I64X",lb.double_value,lb.long_value);
//--- en büyük normalleştirilmiş değer (DBL_MAX)
   lb.long_value=0x7FEFFFFFFFFFFFFF;
   printf("2.  double=%.16e  integer=%I64X",lb.double_value,lb.long_value);
//--- en küçük pozitif normalleştirilmiş değer (DBL_MIN)
   lb.long_value=0x0010000000000000;    
   printf("3.  double=%.16e  integer=%.16I64X",lb.double_value,lb.long_value);
  }
/*  Execution result
    1.  double=-nan(ind)                integer=FFF8000000000000
    2.  double=1.7976931348623157e+308  integer=7FEFFFFFFFFFFFFF
    3.  double=2.2250738585072014e-308  integer=0010000000000000
*/

Birleşimler, programın aynı bellek verilerini farklı şekillerde yorumlamasına izin verdiğinden, genellikle alışılmadık bir tip dönüşümü gerektiğinde kullanılır.

Birleşimler kalıtım'da yer alamazlar ve aynı zamanda doğası gereği statik üyelere sahip olamazlar. Diğer tüm yönleriyle, union, tüm üyeleri sıfır ötelemeye (ofsete) sahip bir yapı gibi davranır. Aşağıdaki tipler birleşim üyesi olamazlar:

Sınıflara benzer şekilde birleşim, yöntemlerin yanı sıra yapıcı ve yıkıcılara da sahip olabilir. Varsayılan olarak, birleşim üyeleri public erişim tipindedir. Özel elemanlar oluşturmak için private anahtar kelimesini kullanın. Tüm bu olasılıklar, color tipindeki bir rengin ARGB'ye nasıl dönüştürüleceğini (ColorToARGB() fonksiyonunun yaptığı gibi) gösteren örnekte sunulmaktadır.

//+------------------------------------------------------------------+
//| color(BGR)'ın ARGB'ye dönüşümü için birleşim                     |
//+------------------------------------------------------------------+
union ARGB
  {
   uchar             argb[4];
   color             clr;
   //--- yapıcılar
                     ARGB(color col,uchar a=0){Color(col,a);};
                    ~ARGB(){};
   //--- genele açık yöntemler
public:
   uchar   Alpha(){return(argb[3]);};
   void    Alpha(const uchar alpha){argb[3]=alpha;};
   color   Color(){ return(color(clr));};
   //--- Özel yöntemler
private:
   //+------------------------------------------------------------------+
   //| alfa kanalı değerini ve rengini ayarla                           |
   //+------------------------------------------------------------------+
   void    Color(color col,uchar alpha)
     {
      //--- clr üyesinin rengini ayarla
      clr=col;
      //--- Alfa bileşen değerini ayarla - opaklık seviyesi
      argb[3]=alpha;
      //--- R ve B bileşenlerinin baytlarını değiş tokuş et (Kırmızı ve Mavi)     
      uchar t=argb[0];argb[0]=argb[2];argb[2]=t;
     };
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 0x55, 55/255=21.6% anlamına gelir (0%, tamamen saydamdır)
   uchar alpha=0x55; 
//--- color tipi 0x00BBGGRR olarak temsil edilir
   color test_color=clrDarkOrange;
//--- ARGB birleşiminden gelen bayt değerleri burada kabul edilir
   uchar argb[]; 
   PrintFormat("0x%.8X - %s, BGR=(%s) için 'color' tipinin nasıl göründüğü belirtilmektedir",
               test_color,ColorToString(test_color,true),ColorToString(test_color));
//--- ARGB tipi 0x00RRGGBB olarak temsil edilir, RR ve BB bileşenleri yer değiştirmiştir
   ARGB argb_color(test_color);
//--- bayt dizisini kopyala
   ArrayCopy(argb,argb_color.argb);
//--- ARGB temsilinde görünümün nasıl olduğu  
   PrintFormat("0x%.8X - alfa kanalı=0x%.2x, ARGB=(%d,%d,%d,%d) ile ARGB temsili",
               argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- opaklık düzeyini ekle
   argb_color.Alpha(alpha);
//--- ARGB'yi 'color' tipi olarak tanımlamayı dene
   Print("color olarak ARGB=(",argb_color.clr,")  alfa kanalı=",argb_color.Alpha());
//--- bayt dizisini kopyala
   ArrayCopy(argb,argb_color.argb);
//--- ARGB temsilinde görünümün nasıl olduğu
   PrintFormat("0x%.8X - alfa kanalı=0x%.2x, ARGB=(%d,%d,%d,%d) ile ARGB temsili",
               argb_color.clr,argb_color.Alpha(),argb[3],argb[2],argb[1],argb[0]);
//--- ColorToARGB() fonksiyonu sonuçları ile kontrol edin
   PrintFormat("0x%.8X - ColorToARGB(%s,0x%.2x) nin sonucu",ColorToARGB(test_color,alpha),
               ColorToString(test_color,true),alpha);
  }
/* Gerçekleşim sonucu
   0x00008CFF - clrDarkOrange için 'color' tipinin nasıl göründüğü belirtilmektedir, BGR=(255,140,0)
  0x00FF8C00 - alfa kanalı=0x00, ARGB=(0,255,140,0) ile ARGB temsili
   color olarak ARGB=(0,140,255)  alfa kanalı=85
   0x55FF8C00 - alfa kanalı=0x55, ARGB=(85,255,140,0) ile ARGB temsili
   0x55FF8C00 - ColorToARGB(clrDarkOrange,0x55)'nin sonucu
*/ 

Arayüzler #

Arayüzler sınıflar tarafından daha sonra kullanılmak üzere belirli bir işlevselliği tanımlamaya yarar. Aslında bunlar üyeleri olmayan sınıflardır yapıcı ve yıkıcı fonksiyonları içeremezler. Arayüzlerin bünyesinde bildirimi yapılan tüm yöntemler –açık tanıma sahip olmasalar bile– sanal yöntemlerdir.

Arayüzler "interface" anahtar sözcüğü ile tanımlanırlar. Örnek:

//--- Hayvanları açıklamak için temel arayüz
interface IAnimal
  {
//--- arayüzün yöntemleri varsayılan olarak genel erişime açık
   void Sound();  // hayvanın çıkardığı ses
  };
//+------------------------------------------------------------------+
//|  CCat sınıfı IAnimal arayüzünden türetilmiştir                   |
//+------------------------------------------------------------------+
class CCat : public IAnimal
  {
public:
                     CCat() { Print("Kedi doğdu"); }
                    ~CCat() { Print("Kedi öldü");  }
   //--- IAnimal arayüzündeki Sound yönteminin uygulanması
   void Sound(){ Print("miyav"); }
  };
//+------------------------------------------------------------------+
//|  CDog sınıfı IAnimal arayüzünden türetilmiştir                   |
//+------------------------------------------------------------------+
class CDog : public IAnimal
  {
public:
                     CDog() { Print("Köpek doğdu"); }
                    ~CDog() { Print("Köpek öldü");  }
   //--- IAnimal arayüzündeki Sound yönteminin uygulanması
   void Sound(){ Print("hav"); }
  };
//+------------------------------------------------------------------+
//| Script programı başlatma fonksiyonu                              |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- IAnimal tipi nesneler için bir işaretçi dizisi
   IAnimal *animals[2];
//--- IAnimal 'dan çocuk sınıfların oluşturulması ve bunların işaretçilerinin bir diziye yüklenmesi    
   animals[0]=new CCat;
   animals[1]=new CDog;
//--- Her çocuk sınıf için IAnimal arayüzünün Sound() yöntemini çağır  
   for(int i=0;i<ArraySize(animals);++i)
      animals[i].Sound();
//--- Nesnelerin silinmesi
   for(int i=0;i<ArraySize(animals);++i)
      delete animals[i];
//--- Çalıştırma sonuçları
/*
  Kedi doğdu
  Köpek doğdu
  miyav
  hav
  Kedi öldü
  Köpek öldü
*/
  }

Soyut sınıflar gibi arayüzler de kalıtım olmadan kullanılamazlar. Sadece diğer arayüzlerden türetilebilirler ve ilgili sınıflara ebeveynlik yapabilirler. Arayüzler her zaman genel görünürlüğe sahiptir.

Bildirimleri sınıfların veya yapıların bildirimlerine dahil edilemez ama arayüzün bir işaretçisi void * tipli bir değişkende saklanabilir. Genel olarak, sınıf nesnesi işaretçileri void * tipli değişkenlerde saklanabilir. void * işaretçisini belli bir sınıf nesnesinin işaretçisine dönüştürmek için dynamic_cast operatörünü kullanın. Dönüşüm mümkün olmadığında, dynamic_cast işlemi NULL ifadesi ile sonuçlanır.

Ayrıca bakınız

Nesne Yönelimli Programlama