Sunum yükleniyor. Lütfen bekleyiniz

Sunum yükleniyor. Lütfen bekleyiniz

SANAL FONKSİYONLAR VE ÇOKBİÇİMLİLİK Yılmaz Kılıçaslan.

Benzer bir sunumlar


... konulu sunumlar: "SANAL FONKSİYONLAR VE ÇOKBİÇİMLİLİK Yılmaz Kılıçaslan."— Sunum transkripti:

1 SANAL FONKSİYONLAR VE ÇOKBİÇİMLİLİK Yılmaz Kılıçaslan

2 GİRİŞ C++, hem derleyici-zamanlı hem de çalışma-zamanlı çok biçimliliği destekler. Derleyici-zamanlı çok biçimlilik fonksiyonların veya operatörlerin aşırı yüklenmesi yoluyla gerçekleştirilir. Çalışma-zamanlı çok biçimlilik ise kalıtım ve sanal fonksiyon mekanizmaları aracılığıyla gerçekleştirilir.

3 TEMEL VE TÜRETİLMİŞ SINIFLAR ARASINDA DÖNÜŞÜM - 1 Bir satış elemanının aynı zamanda bir ücretli çalışan olması nedeniyle, bir UcretliCalisan nesnesine ihtiyaç duyduğumuz zaman bir SatisElemani nesnesi kullanabiliyor olmamız anlamlı olacaktır. Bu tarz bir ilişkiyi desteklemek için, C++ örtük olarak bir türetilmiş sınıf nesnesini bir temel sınıf nesnesine dönüştürür: UcretliCalisan isci; SatisElemani satici( “Ali Uzun” ); isci = satici; // SatisElemani => UcretliCalisan // turetilmis => temel Ters yönlü bir atama kabul edilmeyecektir: satici = isci; // Error; cannot convert

4 TEMEL VE TÜRETİLMİŞ SINIFLAR ARASINDA DÖNÜŞÜM - 2 Benzer şekilde, türetilmiş bir sınıf nesnesine işaret eden bir işaretçi örtük olarak bir temel sınıf nesnesine işaret eden bir işaretçiye dönüştürülebilir: Calisan *cPtr; UcretliCalisan isci( “Oya Kiper” ); SatisElemani satici( “Ali Uzun” ); Yonetici mudur( “Mehmet Demir “ ); cPtr = &isci;//UcretliCalisan * => Calisan * cPtr = &satici;//SatisElemani * => Calisan * cPtr = &mudur;//Yonetici * => Calisan *

5 TEMEL VE TÜRETİLMİŞ SINIFLAR ARASINDA DÖNÜŞÜM - 3 Bir nesneye işaretçi ile referansta bulunulduğu zaman hangi eleman fonksiyonun çağrılacağını işaretçinin tipi belirler. Eğer bir türetilmiş sınıf nesnesine bir temel sınıf işaretçisi ile işaret ederseniz yalnızca temel sınıfa ait fonksiyonları çağırabilirsiniz: SatisElemani satici( “Ali Uzun” ); SatisElemani *sPtr; UcretliCalisan *uPtr; sPtr = &satici; uPtr = &satici; uPtr->sureBelirle(40.0); //UcretliCalisan sPtr->ucretBelirle(6.0); //UcretliCalisan uPtr->satislariBelirle(1000.0); //Error sPtr->satislariBelirle(1000.0); //SatisElemani sPtr->komisyonBelirle(0.05); //SatisElemani float taban, toplam; taban = uPtr->odemeHesapla(); //UcretliCalisan toplam = sPtr->odemeHesapla(); //SatisElemani

6 TEMEL VE TÜRETİLMİŞ SINIFLAR ARASINDA DÖNÜŞÜM - 4 Türetilmiş bir sınıf işaretçisinin bir temel sınıf işaretçisine dönüştürülebilmesi, örneğin, bir çalışanlar topluluğu oluşturmamıza imkan verebilir: Class PersonelListesi {public: PersonelListesi(); ekle( Calisan *yeniPer ); // … }; PersonelListesi bolum1; UcretliCalisan *ucretliPtr; SatisElemani *saticiPtr; Yonetici *yoneticiPtr; // Yeni nesneler için bellek talebi ucretliPtr = new UcretliCalisan(“Ayse Suer” ); saticiPtr = new SatisElemani( “Ali Demir” ); yoneticiPtr = new Yonetici( “Mehmet Yilmaz” ); // Listeye ekleme bolum1.ekle( ucretliPtr ); bolum1.ekle( saticiPtr ); bolum1.ekle( yoneticiPtr ); Buradaki sorun her nesne yalnızca genel bir Calisan düzeyinde işleme tabi olabilir.

7 SANAL FONKSİYONLAR (sentaks) Sanal fonksiyon temel bir sınıfta bildirimi yapılan fakat türetilmiş bir sınıfta yeniden tanımlanmasını beklediğimiz bir eleman fonksiyondur. Bir sanal fonksiyon bildirimi temel sınıftaki eleman fonksiyon bildiriminin öncesine virtua l anahtar sözcüğünü yerleştirerek gerçekleştirilir. Global fonksiyonlar, arkadaş fonksiyonlar ve statik eleman fonksiyonlar sanal fonksiyon olamazlar. virtual anahtar sözcüğü, türetilmiş sınıflardaki bildirimler yada sınıf dışında tanımlanmış sanal fonksiyonlar için gerekli değildir; bir sanal fonksiyonun türetilmiş sınıflardaki versiyonları örtük olarak sanal bildirimlidirler. Türetilmiş sınıftaki sanal fonksiyon bildiriminde yada sınıf dışındaki sanal fonksiyon tanımında virtual anahtar sözcüğünü kullanmak derleyici tarafından bir hata olarak yorumlanmaz.

8 SANAL FONKSİYONLAR (semantik) “Normal” bir erişimde, sanal fonksiyonlar diğer bütün sınıfa ait eleman fonksiyonlar gibi davranırlar. Fakat, sanal fonksiyonları önemli kılan ve çalışma zamanlı çok biçimliliği desteklemesini sağlayan bir işaretçi ya da referans ile erişilmeleri halindeki davranışlarıdır. Eğer bir temel sınıf işaretçisi ya da referansı bu temel sınıftan türetilmiş bir sınıfın nesnesine işaret ediyorsa / referansta bulunuyorsa, sıradan fonksiyonlarda olmasını beklediğimizin tersine, C++ sanal bir fonksiyonun hangi versiyonunu çağıracağına nesneye bakarak karar verir. Bu karar çalışma zamanında gerçekleştirilir. Dolayısıyla, farklı nesnelere işaret edildiğinde / referansta bulunulduğunda fonksiyonun farklı versiyonları çalıştırılacaktır.

9 SANAL FONKSİYONLAR (örnek) //Sanal odemeHesapla fonksiyonlu calisan sinif hiyerarsisi class Calisan { public: Calisan( const char *nm ); char *isimSoyle() const; virtual float odemeHesapla() const; virtual ~Calisan() {} private: char isim[30]; }; class UcretliCalisan : public Calisan { public: UcretliCalisan( const char *nm ); void ucretBelirle( float ucrt ); void sureBelirle( float st ); float odemeHesapla() const; // sanal (ortuk) private: float ucret; float saat; };

10 SANAL FONKSİYONLAR (örnek) class SatisElemani : public UcretliCalisan { public: SatisElemani( const char *nm ); void komisyonBelirle( float kom ); void satislariBelirle( float satislar ); float odemeHesapla() const; // sanal (ortuk) private: float komisyon; float satisMiktari; }; class Yonetici : Calisan { public: Yonetici( const char *nm ); void maasBelirle( float maas ); float odemeHesapla() const; // sanal (ortuk) private: float haftalikMaas; };

11 SANAL FONKSİYONLAR (örnek) Her sınıfın kendisine ait odemeHesapla fonksiyonunun tanımı değişmeden kalabilir. Fakat, temel sınıfa eklenen odemeHesapla fonksiyonu için bir tanım gerekmektedir: float Calisan::odemeHesapla() const { cout << “Hesaplama islemi tanimlanmamis\n”; return 0.0; } Bu fonksiyon eğer salt bir Calisan nesnesi kullanıldığında ya da türetilmiş sınıflardan bir tanesi kendi odemeHesapla fonksiyonunu tanımlamamış ise çağrılır. // Bir Calisan isaretcisi ile odemeHesapla cagrimi Calisan *cPtr; float ucret; cPtr = &isci; ucret = cPtr->odemeHesapla(); //UcretliCalisan cPtr = &satici; ucret = cPtr->odemeHesapla(); //SatisElemani cPtr = &mudur; ucret = cPtr->odemeHesapla(); //Yonetici Eğer odemeHesapla sanal bildirimli olmasaydı, her fonksiyon çağrımı, 0.0 değerini döndüren Calisan::odemeHesapla fonksiyonunu çalıştıracaktı. Fakat, verilen çağrımların aynı olmasına rağmen çalıştırılacak fonksiyonlar farklı olacaktır.

12 SANAL FONKSİYONLAR (örnek) // Bir baska ornek #include using namespace std; class temel { public: virtual void sanalFonk() {cout << “Temel sinif – sanalFonk\n”;} }; class turetilmis1 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif1 – sanalFonk\n”;} }; class turetilmis2 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif2 – sanalFonk\n”;} };

13 SANAL FONKSİYONLAR (örnek) int main() { temel *p, b; turetilmis1 d1; turetilmis2 d2; p = &b; p->sanalFonk(); p = &d1; p->sanalFonk(); p = &d2; p->sanalFonk(); return 0; } Program çıktısını belirleyiniz.

14 SANAL FONKSİYONLAR (örnek) Program çıktısı: Temel sinif – sanalFonk Turetilmis sinif1 – sanalFonk Turetilmis sinif2 – sanalFonk “Normal” yolla sanal fonksiyon çağrımı: d2.sanalFonk; // turetilmis2::sanalFonk

15 SANAL FONKSİYONLAR ve AŞIRI YÜKLEME İlk bakışta benzer görünmesine karşın sanal bir fonksiyonun türetilmiş bir sınıfta yeniden tanımlanması fonksiyonların aşırı yüklenmesi (“function overloading”) işleminden farklıdır. Farkı belirtiniz.

16 SANAL FONKSİYONLAR ve REFERANSLAR - 1 // Temel sinif referansi ile sanal fonksiyon erisimi #include using namespace std; class temel { public: virtual void sanalFonk() {cout << “Temel sinif – sanalFonk\n”;} }; class turetilmis1 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif1 – sanalFonk\n”;} }; class turetilmis2 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif2 – sanalFonk\n”;} };

17 SANAL FONKSİYONLAR ve REFERANSLAR - 2 // Temel sinif referansini parametre alan fonksiyon void f(temel &r) {r.sanalFonk() ; } int main() { temel b; turetilmis1 d1; turetilmis2 d2; f(b); f(d1); f(d2); return 0; } Program çıktısını belirleyiniz.

18 Sanallık Kalıtımla Aktarılır - 1 Sanal bir fonksiyon kalıtıldığında, sanal doğası da kalıtılır: #include using namespace std; class temel { public: virtual void sanalFonk() {cout << “Temel sinif – sanalFonk\n”;} }; class turetilmis1 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif1 – sanalFonk\n”;} }; class turetilmis2 : public turetilmis1 { public: void sanalFonk() {cout << “Turetilmis sinif2 – sanalFonk\n”;} };

19 Sanallık Kalıtımla Aktarılır - 2 int main() { temel *p, b; turetilmis1 d1; turetilmis2 d2; p = &b; p->sanalFonk(); p = &d1; p->sanalFonk(); p = &d2; p->sanalFonk(); return 0; } Bu kez, turetilmis2 adlı sınıf temel adlı sınıftan değil turetilmis1 adlı sınıftan türetilmiştir ama sanalFonk yine sanal kalmıştır. Program çıktısını belirleyiniz.

20 Sanal Fonksiyonlar Hiyerarşiktir - 1 Eğer türetilmiş bir sınıf sanal bir fonksiyonu yeniden tanımlamaz ise bu sınıfın bir nesnesi sanal fonksiyona erişmek istediğinde temel sınıf tarafından tanımlanan versiyon kullanılır: #include using namespace std; class temel { public: virtual void sanalFonk() {cout << “Temel sinif – sanalFonk\n”;} }; class turetilmis1 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif1 – sanalFonk\n”;} }; class turetilmis2 : public temel { public: // sanalFonk() yeniden tanimlanmamis. };

21 Sanal Fonksiyonlar Hiyerarşiktir - 2 int main() { temel *p, b; turetilmis1 d1; turetilmis2 d2; p = &b; p->sanalFonk(); p = &d1; p->sanalFonk(); p = &d2; p->sanalFonk(); return 0; } Program çıktısını belirleyiniz.

22 Sanal Fonksiyonlar Hiyerarşiktir - 3 Program çıktısı: Temel sinif – sanalFonk Turetilmis sinif1 – sanalFonk Temel sinif – sanalFonk Yukarıdaki program genel bir kuralın özel bir durumunu örneklemektedir. Kalıtım, C++’da hiyerarşik olarak tanımlandığı için, sanal fonksiyonlar da hiyerarşik olmalıdır. Bu ise, eğer türetilmiş sınıf sanal fonksiyonu yeniden tanımlamış ise tersinden hiyerarşik sıralamada ilk yeniden tanımlanmış fonksiyonun kullanılacağı anlamına gelir (bkz. bir sonraki örnek).

23 Sanal Fonksiyonlar Hiyerarşiktir - 4 #include using namespace std; class temel { public: virtual void sanalFonk() {cout << “Temel sinif – sanalFonk\n”;} }; class turetilmis1 : public temel { public: void sanalFonk() {cout << “Turetilmis sinif1 – sanalFonk\n”;} }; class turetilmis2 : public turetilmis1 { public: // sanalFonk() yeniden tanimlanmamis. };

24 Sanal Fonksiyonlar Hiyerarşiktir - 5 int main() { temel *p, b; turetilmis1 d1; turetilmis2 d2; p = &b; p->sanalFonk(); p = &d1; p->sanalFonk(); p = &d2; p->sanalFonk(); return 0; } Program çıktısını belirleyiniz.

25 Sanal Fonksiyonlar Hiyerarşiktir - 6 Program çıktısı: Temel sinif – sanalFonk Turetilmis sinif1 – sanalFonk

26 SAF SANAL FONKSİYONLAR - 1 Birçok durumda temel sınıftaki sanal fonksiyon için anlamlı bir tanım bulunmayabilir. Bu durumlarda saf sanal fonksiyonlar (“pure virtual functions”) kullanılır. Saf sanal fonksiyon temel sınıfta tanımı olmayan sanal fonksiyondur ve bildiriminin genel formu aşağıdaki gibidir: virtual tip fonksiyon-ismi(parametreler) = 0; Saf sanal fonksiyon türetilmiş bütün sınıflarda yeniden tanımlanmalıdır. Aksi, derleyici-zamanlı hataya yol açar. En az bir saf sanal fonksiyon içeren sınıfa soyut (“abstract”) sınıf denir. Soyut sınıflara ait nesne yaratılamaz ama soyut sınıflara işaret eden işaretçiler yada referansta bulunan referanslar yaratılabilir.

27 SAF SANAL FONKSİYONLAR - 2 #include using namespace std; class sayi { protected: int deger; public: void degerBelirle(int i) { deger = i;} virtual void goruntule() = 0; }; class hexTip : public sayi { public: void goruntule(){cout << hex << deger << “\n”;} }; class desTip : public sayi { public: void goruntule(){cout << deger << “\n”;} }; class oktTip : public sayi { public: void goruntule(){cout << oct << deger << “\n”;} };

28 SAF SANAL FONKSİYONLAR - 3 int main() { desTip d; hexTip h; oktTip o; d.degerBelirle(20); d.goruntule(); // 20 h.degerBelirle(20); h.goruntule(); // 14 o.degerBelirle(20); o.goruntule(); // 24 return 0; }

29 SANAL YIKICI FONKSİYONLAR Problem: Dinamik olarak yaratılmış nesneleri delete operatörü ile yok etmek potansiyel bir problem kaynağıdır. Eğer delete bir temel sınıf işaretçisine uygulanıyorsa, işaretçi türetilmiş bir sınıfın nesne işaret ediyor bile olsa, derleyici temel sınıfın yıkıcı fonksiyonunu çağıracaktır. Çözüm: Bu sorun temel sınıfın yıkıcı fonksiyonunu sanal yaparak çözülür. Bu şeklide, türetilmiş sınıfların yıkıcı fonksiyonları da sanal yapılmış olacaktır ve böylelikle delete operatörü temel sınıf işaretçisine uygulandığında ilgili yıkıcı fonksiyon çağrılacaktır. İlke olarak sanal fonksiyon içeren bir sınıf yazıldığında, sanal bir yıkıcı fonksiyon da tanımlanmalıdır. Yıkıcı fonksiyonların aksine, yapıcı fonksiyonlar sanal olarak tanımlanamaz.

30 STATİK VE DİNAMİK BAĞLAMA - 1 Fonksiyon çağırma işlemi ile çağrılan fonksiyon kodu arasında bağlantı kurulması işlemi “bağlama” olarak adlandırılır. Bağlama, genellikle derleme esnasında yapılan bir işlemdir. –fonksiyon çağırma komutları ile fonksiyon adresleri birbirine bağlanır; –Fonksiyon çağırma komutundan hangi fonksiyonun çağrılmak istendiğinin anlaşılması gerekir. Program içerisindeki fonksiyon çağıma komutlarından, hangi fonksiyonun çağrılmak istendiğine derleme aşamasında karar verilebilmesi “erken bağlama” veya “statik bağlama” olarak adlandırılır. Bağlama işleminin programın çalıştırılması esnasında yapılmasına “geç bağlama” veya “dinamik bağlama” denir. Bir fonksiyon işaretçisi fonksiyonun bellekteki adresini içerir. Fonksiyon ismi fonksiyona işaret eden bir sabit işaretçidir. –long (*fonksiyon) (int); –long *fonksiyon(int);

31 STATİK VE DİNAMİK BAĞLAMA - 2 #include using namespace std; int artan(int a, int b) { return a>b; } int azalan(int a, int b) { return a<b; } void yer_degistir(int &eleman1, int &eleman2) { int gecici; gecici = eleman1; eleman1 = eleman2; eleman2 = gecici; } void sirala( int *dizi, int boyut, int (*karsilastir)(int,int) ) { int i, j; for (i = 0; i < boyut; i++) for(j = i+1; j < boyut; j++) if ((*karsilastir)(dizi[i], dizi[j])) yer_degistir(dizi[i], dizi[j]); }

32 STATİK VE DİNAMİK BAĞLAMA - 3 int main() { int a[10] = {3,6,12,89,5,43,2,90,345,1}; int siralama; cout << “Artan siralama: 1 - Azalan siralama: 0 “; cin >> siralama; if ( siralama == 1 ) { sirala(a, 10, artan); cout << “Artan dizi: \n”;} else {sirala(a, 10, azalan);cout << “Azalan dizi: \n”;} for (int k = 0; k < 10; k++) cout << a[k] << “ “; return 0; }


"SANAL FONKSİYONLAR VE ÇOKBİÇİMLİLİK Yılmaz Kılıçaslan." indir ppt

Benzer bir sunumlar


Google Reklamları