C++ Dersi:
Nesne Tabanlı Programlama
Çiğdem Turhan Fatma Cemile Serçe
Bölüm 20: İleri Seviye Konular
İçerik
– İleri Düzey Operatör Yükleme
• Yapıcı Fonksiyonların Dönüşüm Operatörü Olarak Kullanımı
• Dönüştürme Operatör Fonksiyonu
• = Atama Operatörü
– Çoklu Miras Kullanırken Karşılaşılan Sorunlar – Arayüz Kavramı
• UML Sınıf Diyagramında Arayüz Gösterimi
• C++ Dilinde Arayüz Tanımı
– Tasarım Örüntüleri
• Tekli Tasarım Örüntüsü
• Strateji Tasarım Örüntüsü
İleri Düzey Operatör Yükleme
• Yapıcı Fonksiyonların Dönüşüm Operatörü Olarak Kullanımı
• Bir sınıfa ait bir nesne, çeşitli tiplerdeki nesne ya da değerlerle bir operatör işlemine girecek ise, aynı operatörün farklı tipteki parametreler için ayrı ayrı tanımlanması gerekir.
• Bu sorunu gidermek için, çok sayıda fonksiyon yazmak yerine, yapıcı fonksiyonlar gerekli dönüşüm işlemini yapmak amacı ile dönüşüm operatörü olarak kullanılabilir.
• Örnek
// --- Zaman.h--- class Zaman
{public:
Zaman(int saat, int dakika, int saniye) {setZaman(saat,dakika,saniye);}
Zaman(long);
friend Zaman operator+
(const Zaman&, const Zaman&);
void setZaman(int saat, int dakika, int saniye);
void goruntule();
private:
int saat;
int dakika;
int saniye;
};
ÇIKTI 1:30:5 2:42:40 Dönüştürme işlemini gerçekleştirecek yapıcı fonksiyon
// --- Zaman.cpp ---
#include <iostream>
#include "Zaman.h"
using namespace std;
inline Zaman::Zaman(long t) { saniye = t % 60;
dakika = (t/60) % 60;
saat = (t/3600) % 24;
}
void Zaman::setZaman(int s, int d, int ss)
{ saat = s;
dakika = d;
saniye = ss;
}
void Zaman::goruntule() { cout<<saat<<":"<<dakika
<<":"<<saniye<<endl;
} // --- Uygulama.cpp ---
#include "Zaman.h"
Zaman operator+(const Zaman& t1, const Zaman& t2)
{ const long saat = t1.saat + t2.saat;
const long dakika = t1.dakika + t2.dakika;
const long saniye = t1.saniye + ,t2.saniye;
return Zaman(saat, dakika, saniye);
}
// --- Uygulama.cpp (devamı)--- int main()
{
Zaman t1(1,30,5);
Zaman t2 = t1 + Zaman(4355);
t1.goruntule();
t2.goruntule();
return 0;
}
Dönüştürme Operatör Fonksiyonu
• Yapıcı fonksiyonların dönüştürme operatörü olarak kullanılmalarının bazı kısıtları vardır.
– Yapıcı fonksiyonlar ile temel bir veri tipinden bir nesneye dönüşüm gerçekleştirilebilir, örneğin long’dan Zaman nesnesine.
– Ancak, herhangi bir nesnenin bir temel veri tipine dönüştürülmesi yapıcı fonksiyonlarla gerçekleştirilemez. Örneğin, Zaman tipinde bir nesne, temel veri tipi olan long’a yapıcı fonksiyonlar ile
dönüştürülemez.
• Bu amaçla dönüştürme operatör fonksiyonlarından yararlanılır.
• Bu fonksiyonlar aşağıdaki söz dizimi ile tanımlanır.
SinifAdı::operator VeriTipi (void)
burada SinifAdı ilgili sınıfı, VeriTipi dönüştürülecek veri tipini
simgeler.
// --- Zaman.h--- class Zaman
{public:
Zaman(int saat, int dakika, int saniye) {setZaman(saat,dakika,saniye);}
Zaman(long);
friend Zaman operator+
(const Zaman&, const Zaman&);
void setZaman(int saat, int dakika, int saniye);
void goruntule();
operator long(void) const;
private:
int saat;
int dakika;
int saniye;
};
ÇIKTI 5435.5 Dönüştürme operatör fonksiyonu
// --- Zaman.cpp ---
#include <iostream>
#include "Zaman.h"
using namespace std;
inline Zaman::Zaman(long t) { saniye = t % 60;
dakika = (t/60) % 60;
saat = (t/3600) % 24;
}
Zaman::operator long(void) const
{ return saat*3600L+dakika*60L+saniye;
}
void Zaman::setZaman(int s, int d, int ss)
{ saat = s;
dakika = d;
saniye = ss;
}
void Zaman::goruntule() { cout<<saat<<":"<<dakika
<<":"<<saniye<<endl;
// --- Uygulama.cpp --- }
#include "Zaman.h"
Zaman operator+(const Zaman& t1, const Zaman& t2)
{ const long saat = t1.saat + t2.saat;
const long dakika = t1.dakika + t2.dakika;
const long saniye = t1.saniye + ,t2.saniye;
return Zaman(saat, dakika, saniye);
// --- Uygulama.cpp (devamı)--- int main()
{
Zaman t1(1,30,5);
double suan = (long)t1 + 30.5;
cout<<suan<<endl;
return 0;
}
= Atama Operatörü
• Atama operatörü (=) özel bir operatör olduğu için bu operatöre yükleme yaparken farklı bir sözdizimi kullanmamız gerekir.
• Aynı sınıfa ait iki nesne arasında atama yapıldığında, tüm üyeler için atama yapılır.
• Bu amaç için derleyici tarafından aşağıdaki sözdizimine uygun olarak bir atama fonksiyonu hazır bulunmaktadır.
SınıfAdı& SınıfAdı::operator= (const SınıfAdı &)
class Nokta {
public:
Nokta& operator=(const Nokta& n) { x = n.x;
y = n. Y;
return *this;
}
private:
int x, y;
};
Atama operatörü
Çoklu Miras Kullanırken Karşılaşılan Sorunlar
• Çoklu miras yöntemi kullanırken dikkat edilmesi gereken bazı noktalar vardır.
– Ortak üst sınıflarda işlevi farklı ama ismi aynı olan
fonksiyonların tanımlı olmasıdır. Derleyici hangi üst sınıfa ait fonksiyonu çağıracağını bilemeyeceği için hata verir.
– Bir sınıfın birden fazla üst sınıftan miras alması, bu üst
sınıfların da ortak bir üst sınıflarının olması durumunda
gerçekleşir. Bu durumda en üst sınıfta tanımlı veri üyeleri,
tekrar tekrar miras olarak en alt sınıfa geçer.
Çoklu Miras Kullanırken Karşılaşılan Sorunlar...
Asistan
Ogrenci Birey
ad soyad tckimlik adres ogrenciNo Ogretmen
Birey
ad
soyad
tckimlik
adres
sicilNo
Sanal Üst Sınıflar
• Ing. Virtual base classes
• Sanal üst sınıflar kullanıldığında eğer ortak bir sınıf üst sınıf olacak ise bu sınıfa ait veri üyelerinden sadece bir kopya tutulur.
• Sanal üst sınıf tanımı aşağıdaki iki şekilde de yapılabilir.
class Ogretmen: virtual public Birey {...}
class Ogrenci: public virtual Birey {...}
• Sanal üst sınıfın kullanımında dikkat edilmesi gereken bir nokta vardır.
Normalde alt sınıfın yapıcı fonksiyonu üst sınıfın yapıcı fonksiyonunu
çağırır. Sanal üst sınıf kullanıldığında ise en alt seviyedeki sınıfın (Asistan)
dahi en üst seviyedeki sınıfın (Birey) yapıcı fonksiyonunu çağırması gerekir.
Arayüz
• Ing. Interface
• Bir arayüz sınıfı, o sınıfa ait üye fonksiyonların sadece prototiplerini içeren soyut bir sınıftır.
• Bu arayüzü uygulayan sınıfların, arayüzde belirtilen prototiplere göre fonksiyonların üzerine yazması zorunludur.
– UML sınıf diyagramında oklu kezik çizgi ile belirtilir.
implements (uygular) ya da realize anahtar kelimeleri
kullanılır.
Örnek
• Gosterilebilir bir arayüz sınıfıdır ve goster() fonksiyon prototipini tanımlar.
• Sekil sınıfı Gosterilebilir arayüzünü
gerçekleştirdiği için goster() üye fonksiyonuna sahiptir.
• Belge sınıfı benzer şekilde Gosterilebilir
arayüzünü gerçekleştirmektedir ve dolayısı ile goster() üye fonksiyonuna sahiptir.
• Sekil sınıfı içinde goster() fonksiyonunun içeriği verilemeyeceği için (çünkü şekle göre gösterim değişecektir), alt sınıflar bu fonksiyonun üzerine yazarlar. Bu nedenle Sekil sınıfında bu fonksiyon sanal fonksiyon olarak tanımlanmıştır.
• Kare ve Cizgi sınıfları Sekil sınıfı ile aralarındaki
miras ilişkisi sayesinde Gosterilebilir sınıflardır ve
goster() fonksiyonuna sahiptirler.
Tasarım Örüntüsü
• Bir yazılım tasarımında sıklıkla karşılaşılan problemlere yönelik geliştirilmiş ortak çözüm önerisi sunar.
• Bu çözüm önerileri, bir problemin nasıl çözülebileceğini tasarım ve uygulama boyutunda tanımlar.
• Bir tasarım örüntüsü, bir sınıf diyagramı ile tanımlanır ve hangi durumlarda kullanılabileceği anlatılır.
• Örnek
– Tekli Tasarım Örüntüsü (Singleton)
– Strateji Tasarım Örüntüsü
Tekli Tasarım Örüntüsü
• Bazı problemlere ait yazılım tasarımlarında, aynı sınıftan ikinci bir nesnenin yaratılmaması gereken durumlar olabilir.
• Bu tür durumlarda bu tasarım örüntüsü, aynı sınıftan sadece
tek bir nesnenin yaratılmasını garanti eden bir çözüm önerisi
sunar.
Tekli Tasarım Örüntüsü...
• Bu tasarım örüntüsünü uygulamak için aşağıdaki adımların izlenmesi gereklidir.
– Sınıfa ait yapıcı fonksiyonların private ya da protected etiketleri ile etiketlenerek dışarıdan erişimin engellenmesi,
– Sınıf içerisinde yaratılacak tek nesneyi gösterecek göstergenin private static etiket ile tanımı,
– Tek nesneye dışarıdan erişilmesini sağlayan public static üye fonksiyonun yazılması
– Kopya yapıcı fonksiyonun üzerine yazılıp boş bırakılması
//--- Butce.h --- class Butce
{
public:
static Butce* nesneAl();
void gelirEkle(float _gelir) {gelir +=_gelir;}
float gelirAl(){return gelir;}
void giderEkle(float _gider) {gider +=_gider;}
float giderAl(){return gider;}
protected:
Butce(float gelir=0, float gider=0){}
private:
static Butce* nesne;
float gelir, gider;
};
ÇIKTI
Gelir(Butce-1):2500 Gelir(Butce-2):2500 Gider(Butce-1):1200 Gider(Butce-2):1200
//--- Butce.cpp ---
#include "Butce.h"
Butce* Butce::nesne = 0;
Butce* Butce::nesneAl() {
if(nesne==0){
nesne = new Butce;
}
return nesne;
};
//--- Uygulama.cpp ---
#include "Butce.h"
#include <iostream>
using namespace std;
int main() {
Butce* butce1= Butce::nesneAl();
Butce* butce2= Butce::nesneAl();
butce1->gelirEkle(1500);
butce1->gelirEkle(1000);
butce2->giderEkle(700);
butce2->giderEkle(500);
cout<<"Gelir(Butce-1):"<<butce1->gelirAl()<<endl;
cout<<"Gelir(Butce-2):"<<butce2->gelirAl()<<endl;
cout<<"Gider(Butce-1):"<<butce1->giderAl()<<endl;
cout<<"Gider(Butce-2):"<<butce2->giderAl()<<endl;
return 0;
}
Strateji Tasarım Örüntüsü
• Bir yazılım uygulamasına ait gereksinimde, aynı işin farklı algoritma ve yöntemlerle yapılması isteniyor ve ileride yeni yöntemlerin de eklenme ihtimali var ise, çözüme ait tasarımın bu kapsamda esnek bir yapıda kurgulanması tercih edilir.
• Strateji tasarım örüntüsü bu amaçla kullanılan bir çözüm
önerisidir.
Örnek
• Ekran sınıfı alt seviyedeki Sekil, Belge, Kare ve Cizgi sınıfları ile değil, en üst seviyedeki Gosterilebilir
arayüzü ile bağlıdır. Böylelikle, Ekran sınıfı gösterilebilir olan her nesne ile çalışabilecek, yeniden
kullanılabilir bir sınıf olarak tasarlanabilmiştir. Ekran nesnesine bir Kare nesnesi gönderildiğinde kare
gösterecek, benzer şekilde Belge nesnesi gönderildiğinde belgedeki metni gösterecektir.
//--- Ekran.h ---
#include "Gosterilebilir.h"
class Ekran {
Gosterilebilir* gosterilebilirNesne;
public:
void gosterilebilirNesneAta (Gosterilebilir*);
void nesneGoster();
};
//--- Ekran.cpp---
#include "Ekran.h"
void Ekran::gosterilebilirNesneAta
(Gosterilebilir* _gosterilebilirNesne) { gosterilebilirNesne =
_gosterilebilirNesne;
}
void Ekran::nesneGoster()
{ gosterilebilirNesne->goster();
}
//--- Uygulama.cpp---
#include "Sekil.h"
#include "Kare.h"
#include "Cizgi.h"
#include "Belge.h"
#include "Ekran.h"
#include <iostream>
using namespace std;
int main() {
Kare kare(7);
Cizgi cizgi(7);
Belge belge("Merhaba");
Ekran ekran;
ekran.gosterilebilirNesneAta(&kare);
ekran.nesneGoster();
ekran.gosterilebilirNesneAta(&cizgi);
ekran.nesneGoster();
ekran.gosterilebilirNesneAta(&belge);
ekran.nesneGoster();
return 0;
}
ÇIKTI
*******
* *
* *
* *
* *
* *
*******
*******