İşlemin Aşırı Yüklenmesi
Kodun okunmasını ve yazılmasını kolaylaştırması için aşırı yüklemeye izin verilir. Aşırı yükleme operatörü operator anahtar sözcüğü ile yazılır. Şu operatörler aşırı yüklenebilir:
- ikili +,-,/,*,%,<<,>>,==,!=,<,>,<=,>=,=,+=,-=,/=,*=,%=,&=,|=,^=,<<=,>>=,&&,||,&,|,^
- tekli +,-,++,--,!,~
- atama operatörü =
- indisleme operatörü []
İşlemin aşırı yüklenmesi, karmaşık nesneler - yapılar ve sınıflar için (basit ifadeler şeklinde yazılmış) işlem notasyonunun kullanılmasını sağlar. Kodu aşırı yüklenmiş operatörler kullanarak yazmak kaynak kodunun görünümünü basitleştirir, çünkü daha karmaşık uygulamalar gizlenecektir.
Örneğin, gerçel ve imajiner kısımlardan meydana gelen karmaşık sayıları ele alalım. Bu sayılar matematikte sıklıkla kullanılırlar. MQL5 dili içerisinde karmaşık sayıları temsil eden bir veri tipi yer almaz ama bir yapı veya sınıf şeklinde yeni bir tip oluşturmak mümkündür. Karmaşık yapının bildirimi ve dört aritmetik işlem gerçekleştiren dört ayrı yöntemin tanımı:
//+------------------------------------------------------------------+
//| Karmaşık sayılı işlemler için bir yapı |
//+------------------------------------------------------------------+
struct complex
{
double re; // Gerçel kısım
double im; // İmajiner kısım
//--- Yapıcılar
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Aritmetik işlemler
complex Add(const complex &l,const complex &r) const; // Toplama
complex Sub(const complex &l,const complex &r) const; // Çıkarma
complex Mul(const complex &l,const complex &r) const; // Çarpma
complex Div(const complex &l,const complex &r) const; // Bölme
};
|
Şimdi, kodumuzda karmaşık sayıları temsil eden değişkenleri bildirilebilir, ardından bunlarla çalışabiliriz.
Örneğin:
void OnStart()
{
//--- Karmaşık tipli değişkenleri bildir ve başlat
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- İki sayıyı topla
complex z;
z=a.Add(a,b);
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- İki sayıyı çarp
z=a.Mul(a,b);
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- İki sayıyı böl
z=a.Div(a,b);
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
|
Ama, alışıldık aritmetik işlemleri karmaşık sayılarla yapmak için; "+", "-", "*" ve "/" şeklindeki alışıldık operatörleri kullanmak daha uygun olacaktır.
'operator' anahtar kelimesi, dönüşümü gerçekleştiren bir üye fonksiyonunu tanımlamak için kullanılır. Sınıf nesnesi değişkenleri için yapılan tekli ve ikili işlemler, statik olmayan üye fonksiyonları gibi aşırı yüklenmiş olabilirler. Bunlar sınıf nesnesi üzerinde gizlice hareket ederler.
Bir sınıf nesnesi veya bu sınıfın bir nesnesinin işaretçisi şeklinde bir veya iki argümanı olan düzenli fonksiyonlar gibi, bir çok ikili işlem de aşırı yüklenebilir. Bizim oluşturduğumuz tip (complex) için, bildirim içindeki aşırı yükleme şöyle gözükecektir:
//--- Operatörler
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
|
Betik örneğinin bütünü:
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- complex tipindeki değişkenleri bildir ve başlat
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//a.re=5;
//a.im=1;
//b.re=-1;
//b.im=-5;
//--- İki sayıyı topla
complex z=a+b;
PrintFormat("a+b=%.2f+i*%.2f",z.re,z.im);
//--- İki sayıyı çarp
z=a*b;
PrintFormat("a*b=%.2f+i*%.2f",z.re,z.im);
//--- İki sayıyı böl
z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//---
}
//+------------------------------------------------------------------+
//| Karmaşık sayılı işlemler için bir yapı |
//+------------------------------------------------------------------+
struct complex
{
double re; // Gerçel kısım
double im; // İmajiner kısım
//--- Yapıcılar
complex():re(0.0),im(0.0) { }
complex(const double r):re(r),im(0.0) { }
complex(const double r,const double i):re(r),im(i) { }
complex(const complex &o):re(o.re),im(o.im) { }
//--- Aritmetik işlemler
complex Add(const complex &l,const complex &r) const; // Toplama
complex Sub(const complex &l,const complex &r) const; // Çıkarma
complex Mul(const complex &l,const complex &r) const; // Çarpma
complex Div(const complex &l,const complex &r) const; // Bölme
//--- İkili operatörler
complex operator+(const complex &r) const { return(Add(this,r)); }
complex operator-(const complex &r) const { return(Sub(this,r)); }
complex operator*(const complex &r) const { return(Mul(this,r)); }
complex operator/(const complex &r) const { return(Div(this,r)); }
};
//+------------------------------------------------------------------+
//| Toplama |
//+------------------------------------------------------------------+
complex complex::Add(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re+r.re;
res.im=l.im+r.im;
//--- Sonuç
return res;
}
//+------------------------------------------------------------------+
//| Çıkarma |
//+------------------------------------------------------------------+
complex complex::Sub(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re-r.re;
res.im=l.im-r.im;
//--- Sonuç
return res;
}
//+------------------------------------------------------------------+
//| Çarpma |
//+------------------------------------------------------------------+
complex complex::Mul(const complex &l,const complex &r) const
{
complex res;
//---
res.re=l.re*r.re-l.im*r.im;
res.im=l.re*r.im+l.im*r.re;
//--- Sonuç
return res;
}
//+------------------------------------------------------------------+
//| Bölme |
//+------------------------------------------------------------------+
complex complex::Div(const complex &l,const complex &r) const
{
//--- Boş karmaşık sayılar
complex res(EMPTY_VALUE,EMPTY_VALUE);
//--- Sıfır için kontrol et
if(r.re==0 && r.im==0)
{
Print(__FUNCTION__+": sayı sıfıra eşittir");
return(res);
}
//--- Yardımcı değişkenler
double e;
double f;
//--- Hesaplama Türevinin seçimi
if(MathAbs(r.im)<MathAbs(r.re))
{
e = r.im/r.re;
f = r.re+r.im*e;
res.re=(l.re+l.im*e)/f;
res.im=(l.im-l.re*e)/f;
}
else
{
e = r.re/r.im;
f = r.im+r.re*e;
res.re=(l.im+l.re*e)/f;
res.im=(-l.re+l.im*e)/f;
}
//--- Sonuç
return res;
}
|
Bir tekil sınıf nesnesini veya bunun işaretçisini argümanı olarak kabul eden normal fonksiyonlar gibi, sınıflar için gerçekleştirilen bir çok tekli işlem de aşırı yüklenmiş olabilir. "-" ve "!" tekli işlemlerinin aşırı yüklenmesini ekle.
//+------------------------------------------------------------------+
//| Karmaşık sayılı işlemler için bir yapı |
//+------------------------------------------------------------------+
struct complex
{
double re; // Gerçel kısım
double im; // İmajiner kısım
...
//--- Tekli operatörler
complex operator-() const; // Tekli eksi
bool operator!() const; // Red
};
...
//+------------------------------------------------------------------+
//| "Tekil eksi" operatörünün aşırı yüklenmesi |
//+------------------------------------------------------------------+
complex complex::operator-() const
{
complex res;
//---
res.re=-re;
res.im=-im;
//--- Sonuç
return res;
}
//+------------------------------------------------------------------+
//| "Mantıksal red" operatörünün aşırı yüklenmesi |
//+------------------------------------------------------------------+
bool complex::operator!() const
{
//--- Karmaşık sayının gerçel ve imajiner kısımları sıfır mı?
return (re!=0 && im!=0);
}
|
Artık karmaşık sayıyı sıfır değeri için kontrol edebilir ve red değeri alabiliriz:
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- complex tipindeki değişkenleri bildir ve başlat
complex a(2,4),b(-4,-2);
PrintFormat("a=%.2f+i*%.2f, b=%.2f+i*%.2f",a.re,a.im,b.re,b.im);
//--- İki sayıyı böl
complex z=a/b;
PrintFormat("a/b=%.2f+i*%.2f",z.re,z.im);
//--- Varsayılan olarak bir karmaşık sayı sıfıra eşittir (Yani re==0 ve im==0)
complex zero;
Print("!zero=",!zero);
//--- Negatif değer ata
zero=-z;
PrintFormat("z=%.2f+i*%.2f, zero=%.2f+i*%.2f",z.re,z.im, zero.re,zero.im);
PrintFormat("-zero=%.2f+i*%.2f",-zero.re,-zero.im);
//--- Bir kez daha sıfır için kontrol et
Print("!zero=",!zero);
//---
}
|
Basit tipli yapıların birinden diğerine doğrudan kopyalanabildiği düşünüldüğünde, "=" atama operatörüne aşırı yükleme yapmamızın gerekli olmadığı not edilmelidir. Artık, karmaşık sayıları alışıldık tarzda kullanan hesaplamalar için bir kod yazabiliriz.
İndisleme operatörünün aşırı yüklenmesi, nesneye iliştirilmiş dizilerin değerlerinin basitçe alınmasını sağlar. Ayrıca kodun okunabilirliğini artırmaya da yardımcı olur. Örneğin, dizgi içinde belirli konumdaki bir sembole erişim sağlamamız gerekebilir. Dizgi, MQL5 içinde ayrı bir tiptir (string), sembollerden oluşan bir dizi değildir. Ama aşırı yüklenmiş bir indisleme operatörünün yardımıyla, CString sınıfı içerisinde basit ve saydam bir çalışma sağlanabilir:
//+---------------------------------------------------------------------------------+
//| Bir dizgideki sembollere, sembol dizisiymiş gibi erişim sağlamak için bir sınıf |
//+---------------------------------------------------------------------------------+
class CString
{
string m_string;
public:
CString(string str=NULL):m_string(str) { }
ushort operator[] (int x) { return(StringGetCharacter(m_string,x)); }
};
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Bir dizgiden sembolleri almak için bir dizi
int x[]={ 19,4,18,19,27,14,15,4,17,0,19,14,17,27,26,28,27,5,14,
17,27,2,11,0,18,18,27,29,30,19,17,8,13,6 };
CString str("abcdefghijklmnopqrstuvwxyz[ ]CS");
string res;
//--- str değişkeni içindeki sembolleri kullanarak bir cümle oluştur
for(int i=0,n=ArraySize(x);i<n;i++)
{
res+=ShortToString(str[x[i]]);
}
//--- Sonucu göster
Print(res);
}
|
İndisleme operatörünün aşırı yüklenmesine bir örnek de matris işlemleridir. Matris iki boyutlu bir dinamik diziyi ifade eder ama dizi büyüklüğü peşin olarak tanımlanmaz. Bu yüzden, array[][] şeklinde bir diziyi, ikinci boyutun büyüklüğünü tanımlamadan ve diziyi bir parametre olarak geçirmeden bildiremezsiniz. Bu soruna getirilebilecek özel bir çözüm CMatrix sınıfıdır. Bu, CRow sınıf nesnelerinin bir dizisini içerir.
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Matrislerin toplanması ve çarpımı işlemleri
CMatrix A(3),B(3),C();
//--- Satılar için bir dizi hazırla
double a1[3]={1,2,3}, a2[3]={2,3,1}, a3[3]={3,1,2};
double b1[3]={3,2,1}, b2[3]={1,3,2}, b3[3]={2,1,3};
//--- Matrisleri doldur
A[0]=a1; A[1]=a2; A[2]=a3;
B[0]=b1; B[1]=b2; B[2]=b3;
//--- Matrisleri Uzmanlar günlüğüne çıktıla
Print("---- Matris A elemanları");
Print(A.String());
Print("---- Matris B elemanları");
Print(B.String());
//--- Matrislerin toplamı
Print("---- A ve B matrislerinin toplamı");
C=A+B;
//--- Biçimlendirilmiş dizgi ifadesini çıktıla
Print(C.String());
//--- Matrislerin çarpımı
Print("---- A ve B matrislerinin çarpımı");
C=A*B;
Print(C.String());
//--- Şimdi değerleri matrix[i][j] dinamik dizileri şeklinde nasıl elde edeceğimizi gösterelim
Print("C matrisi değerlerini elementsel olarak çıktıla");
//--- Matris satırlarını - CRow nesneleri - bir döngü içinde incele
for(int i=0;i<3;i++)
{
string com="| ";
//--- Değer için matris satırlarından
for(int j=0;j<3;j++)
{
//--- Matris elemanlarını satır ve sütun numarası kullanarak al
double element=C[i][j];// [i] - m_rows[] dizisi içinde CRow'a erişim,
// [j] - CRow içinde yapılan indislemenin aşırı yükleme operatörü
com=com+StringFormat("a(%d,%d)=%G ; ",i,j,element);
}
com+="|";
//--- Satır değerlerinin çıktısını al
Print(com);
}
}
//+------------------------------------------------------------------+
//| "Row" sınıfı |
//+------------------------------------------------------------------+
class CRow
{
private:
double m_array[];
public:
//--- Yapıcılar ve bir yıkıcı
CRow(void) { ArrayResize(m_array,0); }
CRow(const CRow &r) { this=r; }
CRow(const double &array[]);
~CRow(void){};
//--- Satırdaki elemanların sayısı
int Size(void) const { return(ArraySize(m_array));}
//--- Değerleriyle bir dizgiye dönüş yapar
string String(void) const;
//--- İndisleme operatörü
double operator[](int i) const { return(m_array[i]); }
//--- Atama operatörleri
void operator=(const double &array[]); // Bir dizi
void operator=(const CRow & r); // Başka bir CRow nesnesi
double operator*(const CRow &o); // Çarpım için CRow nesnesi
};
//+------------------------------------------------------------------+
//| Tek satırlı bir dizinin başlatılması için yapıcı |
//+------------------------------------------------------------------+
void CRow::CRow(const double &array[])
{
int size=ArraySize(array);
//--- Eğer dizi boş değilse
if(size>0)
{
ArrayResize(m_array,size);
//--- Değerler ile doldur
for(int i=0;i<size;i++)
m_array[i]=array[i];
}
//---
}
//+------------------------------------------------------------------+
//| Dizi için atama işlemi |
//+------------------------------------------------------------------+
void CRow::operator=(const double &array[])
{
int size=ArraySize(array);
if(size==0) return;
//--- Diziyi değerlerle doldur
ArrayResize(m_array,size);
for(int i=0;i<size;i++) m_array[i]=array[i];
//---
}
//+------------------------------------------------------------------+
//| CRow için atama operatörü |
//+------------------------------------------------------------------+
void CRow::operator=(const CRow &r)
{
int size=r.Size();
if(size==0) return;
//--- Diziyi değerlerle doldur
ArrayResize(m_array,size);
for(int i=0;i<size;i++) m_array[i]=r[i];
//---
}
//+------------------------------------------------------------------+
//| Başka bir satırla çarpma operatörü |
//+------------------------------------------------------------------+
double CRow::operator*(const CRow &o)
{
double res=0;
//--- Doğrulamalar
int size=Size();
if(size!=o.Size() || size==0)
{
Print(__FUNCSIG__,": İki matrisin çarpımı başarısız oldu, büyüklükler farklı");
return(res);
}
//--- Dizileri elementsel olarak çarp ve çarpımları ekle
for(int i=0;i<size;i++)
res+=m_array[i]*o[i];
//--- Sonuç
return(res);
}
//+------------------------------------------------------------------+
//| Biçimlendirilmiş bir dizgi ifadesine dönüş yapar |
//+------------------------------------------------------------------+
string CRow::String(void) const
{
string out="";
//--- Dizi büyüklüğü sıfırdan büyükse
int size=ArraySize(m_array);
//--- Dizinin sadece sıfır olmayan elemanlarıyla çalışacağız
if(size>0)
{
out="{";
for(int i=0;i<size;i++)
{
//--- Değerleri dizgiye topla
out+=StringFormat(" %G;",m_array[i]);
}
out+=" }";
}
//--- Sonuç
return(out);
}
//+------------------------------------------------------------------+
//| "Matrix" sınıfı |
//+------------------------------------------------------------------+
class CMatrix
{
private:
CRow m_rows[];
public:
//--- Yapıcılar ve bir yıkıcı
CMatrix(void);
CMatrix(int rows) { ArrayResize(m_rows,rows); }
~CMatrix(void){};
//--- Matris büyüklüklerini al
int Rows() const { return(ArraySize(m_rows)); }
int Cols() const { return(Rows()>0? m_rows[0].Size():0); }
//--- Sütun değerini CRow satırı şeklinde dönüş yapar
CRow GetColumnAsRow(const int col_index) const;
//--- Matris değerli bir dizgiye dönüş yapar
string String(void) const;
//--- İndisleme operatörü numarası ile bir dizgiye dönüş yapar
CRow *operator[](int i) const { return(GetPointer(m_rows[i])); }
//--- Toplama operatörü
CMatrix operator+(const CMatrix &m);
//--- Çarpma operatörü
CMatrix operator*(const CMatrix &m);
//--- Atama operatörü
CMatrix *operator=(const CMatrix &m);
};
//+--------------------------------------------------------------------+
//| Ön tanılı bir yapıcı, sıfır büyüklüğünde bir satır dizisi oluştur |
//+--------------------------------------------------------------------+
CMatrix::CMatrix(void)
{
//--- Matristeki sıfır sayılı satırlar
ArrayResize(m_rows,0);
//---
}
//+------------------------------------------------------------------+
//| CRow şeklindeki sütun değerine dönüş yapar |
//+------------------------------------------------------------------+
CRow CMatrix::GetColumnAsRow(const int col_index) const
{
//--- Sütundan değerleri almak için bir değişken
CRow row();
//--- Matristeki satırların sayısı
int rows=Rows();
//--- satır sayısı sıfırdan büyükse, işlemi başlat
if(rows>0)
{
//--- col_index indisli sütun değerlerinin alımı için bir dizi
double array[];
ArrayResize(array,rows);
//--- Dizinin doldurulması
for(int i=0;i<rows;i++)
{
//--- i satırı için sütun sayısını kontrol et - dizi sınırlarını aşabilir
if(col_index>=this[i].Size())
{
Print(__FUNCSIG__,": Hata! Sütun numarası ",col_index,"> satır büyüklüğü ",i);
break; // satır, başlatılmamış nesne olacak
}
array[i]=this[i][col_index];
}
//--- Dizi değerlerine göre bir CRow satırı oluştur
row=array;
}
//--- Sonuç
return(row);
}
//+------------------------------------------------------------------+
//| İki matrisin toplanması |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator+(const CMatrix &m)
{
//--- Geçirilmiş matristeki satır ve sütunların sayısı
int cols=m.Cols();
int rows=m.Rows();
//--- Toplama sonuçlarının alınacağı matris
CMatrix res(rows);
//--- Matrislerin büyüklükleri eşleşmeli
if(cols!=Cols() || rows!=Rows())
{
//--- Toplama imkansız
Print(__FUNCSIG__,": Matrislerin toplanması başarısız, büyüklükleri uyuşmuyor");
return(res);
}
//--- Yardımcı dizi
double arr[];
ArrayResize(arr,cols);
//--- Toplanacak satırları incele
for(int i=0;i<rows;i++)
{
//--- Matris dizgilerinin toplamlarını diziye yaz
for(int k=0;k<cols;k++)
{
arr[k]=this[i][k]+m[i][k];
}
//--- Diziyi matris satırına yerleştir
res[i]=arr;
}
//--- Matrislerin toplama sonucuna dönüş yap
return(res);
}
//+------------------------------------------------------------------+
//| İki matrisin çarpımı |
//+------------------------------------------------------------------+
CMatrix CMatrix::operator*(const CMatrix &m)
{
//--- İlk matristeki sütunların sayısı, matrise geçirilmiş satırların sayısı
int cols1=Cols();
int rows2=m.Rows();
int rows1=Rows();
int cols2=m.Cols();
//--- Toplama sonuçlarını alacak matris
CMatrix res(rows1);
//--- Matrisler koordine edilmeli
if(cols1!=rows2)
{
//--- Çarpma işlemi imkansız
Print(__FUNCSIG__,": Matrislerin çarpımı başarısız oldu, biçim uyumsuz "
"- ilk matristeki sütun sayısı ikinci matrisin satır sayısına eşit olmalı");
return(res);
}
//--- Yardımcı dizi
double arr[];
ArrayResize(arr,cols1);
//--- Çarpım matrisinin satırlarını doldur
for(int i=0;i<rows1;i++)// Satırları incele
{
//--- Alınan diziyi sıfırla
ArrayInitialize(arr,0);
//--- Satırdaki elemanları incele
for(int k=0;k<cols1;k++)
{
//--- Matrisin k sütununun değerlerini CRow şeklinde al
CRow column=m.GetColumnAsRow(k);
//--- iki satırı skaler olarak çarp ve vektörlerin çarpım sonucunu i-inci elemanda yaz
arr[k]=this[i]*column;
}
//--- Matrisin i-inci satırına arr[] dizisini yerleştir
res[i]=arr;
}
//--- Matrislerin çarpımına dönüş yap
return(res);
}
//+------------------------------------------------------------------+
//| Atama işlemi |
//+------------------------------------------------------------------+
CMatrix *CMatrix::operator=(const CMatrix &m)
{
//--- Satır sayısını bul ve ayarla
int rows=m.Rows();
ArrayResize(m_rows,rows);
//--- Satırlarımızı, geçirilmiş matrisin değerleri ile doldur
for(int i=0;i<rows;i++) this[i]=m[i];
//---
return(GetPointer(this));
}
//+------------------------------------------------------------------+
//| Matrisin dizgi şeklinde gösterimi |
//+------------------------------------------------------------------+
string CMatrix::String(void) const
{
string out="";
int rows=Rows();
//--- Dizgileri tek tek şekillendir
for(int i=0;i<rows;i++)
{
out=out+this[i].String()+"\r\n";
}
//--- Sonuç
return(out);
}
|
Ayrıca Bakınız
Aşırı Yükleme, Aritmetik İşlemler, Fonksiyonun Aşırı Yüklenmesi, Öncelik Kuralları