Sanal Bitiş
Fonksiyonları(virtual destructor)
Bir
sınıfın bitiş fonksiyonu sanal olabilir. Aslında ne zaman bir türetme
yapılacaksa taban sınıfın bitiş fonksiyonu sanal yapılmalıdır. Taban sınıfın
bitiş fonksiyonu sanal yapılırsa o sınıftan türetilen tüm sınıfların bitiş
fonksiyonları otomatik olarak sanal kabul edilir. Normal olarak delete
operatörünün operandı hangi sınıfa ilişkin bir adres ise o sınıfa ilişkin bitiş
fonksiyonu çağırılır. Ancak bazı durumlarda adrese ilişkin sınıfın bitiş
fonksiyonu değil de nesnenin orijinaline ilişkin sınıfın bitiş fonksiyonunun
çağırılması gerekir.

B sınıfı A sınıfından
türetilmiş olsun;
{
A *p;
p = new
B(n);
delete
p;
}
Burada normal olarak p
göstericisi A sınıfına ilişkin olduğu için delete p; işlemindeA sınıfının
bitişi fonksiyonu çağırılır. Oysa B sınıfına ilişkin bitiş fonksiyonunun
çağırılması uygun olan durumdur. İşte taban sınıf bitiş fonksiyonu sanal
yapılırsa B sınıfına ilişkin bitiş fonksiyonu çağırılır. Bu bitiş fonksiyonu
kendi içerisinde zaten A sınıfının bitiş fonksiyonunu da çağıracaktır. Bir
sınıf kendisinden türetme yapılacak şekilde tasarlanıyorsa mutlaka bitiş
fonksiyonu sanal yapılmalıdır.
iostream
Sınıf Sistemi
Bu
sınıf sistemi ekran, klavye ve dosya işlemleri için türetilmiş bir dizi
sınıftan oluşur.

istream sınıfı klavye ve
dosyadan okuma yapmak için gereken veri elemanlarına ve üye fonksiyolarına
sahiptir. ostream sınıfı ise ekrana ve dosyaya bilgi yazmak için gereken veri
elemanlarına ve üye fonksiyonlarına sahiptir. ios sınıfı okuma ve yazma
işlemlerinde kullanılan temel ve ortak veri elemanlarını ve üye fonksiyonlarını
bulunduran bir sınıftır. ostream sınıfının her türden parametreye sahip bir
grup << (sola shift) operatör fonksiyonu vardır. Bu operatör
fonksiyonları parametreleri ile belirtileni ekrana yazdırırlar. Yani ostream
sınıfı türünden bir sınıf nesnesi tanımlanır ve bu operatör fonksiyonları
kullanılırsa ekrana yazdırma yapılabilir.
ostream x;
x << 100;
Ancak zaten kütüphane
içerisinde cout isimli bir nesne tanımlanmıştır. Yani bu nesne kullanılarak ekrana
yazdırma yapılabilir. iostream sınıf sisteminin bütün bildirimler iostream.h
içerisindedir. Sınıfların üye fonksiyonları kütüphane içerisindedir.
ostream
sınıfının << operatör fonksiyonlarının geri dönüş değerleri yine ostream
türünden bir referanstır. Böylece bu operatör fonksiyonu birden fazla eleman
için kombine edilebilir.
/*-----cout.cpp-----*/
#include
<iostream.h>
void main(void)
{
int a = 123;
cout <<
"Value=" << 20 << '\n';
}
/*-------------------*/
Sınıf İçerisinde Başka Bir
Sınıf, Yapı, typedef ve enum Bildirimlerinin Bulunması
Bir
sınıfın içerisinde yapı, başka bir sınıf, enum vs. bildirimleri yapılabilir.
Genel olarak sınıf içerisinde bildirilen bütün değişken isimleri(yapı, sınıf,
enum sabitleri gibi) dışarıdan ancak çözünürlük operatörü kullanılarak sınıf
ismiyle erişilebilir. Tabii bu erişimin geçerli olabilmesi için bildirimin
sınıfın public bölümde yapılmış olması gerekir. Yani sınıf içerisinde
bildirilen bütün değişkenler sınıf faaliyet alanına sahip olur. Sınıfın üye fonksiyonları
içerisinde doğrudan, dışarıdan ancak sınıf ismi ve çözünürlük operatörüyle
çağırılabilir.
class X {
private:
int x;
public:
typedef unsigned int WORD;
void func(void);
};
void X::fonk(void)
{
WORD
x; /*Doğru*/
}
void main(void)
{
WORD
x; /*Yanlış*/
X::WORD
x; /*Doğru*/
}
iostream Sınıf Sisteminde
Formatlı Yazdırma İşlemleri
ios
sınıfının protected bölümünde long bir x_flags isimli bir değişken vardır.
ostream sınıfının << operatör fonksiyonları x_flags değişkeninin
bitlerine bakarak yazdırma işleminin nasıl yapılacağını anlarlar. Bu değişkenin
değerini alan ve değişkenin değerini değiştiren iki public üye fonksiyonu
vardır:
long flags();
long flags(long);
x_flags değişkeninin ilgili
bitlerini set edebilmek için ios sınıfı içerisinde bütün bitleri 0 yalnızca bir
biti 1 olan çeşitli sembolik sabitler enum sabiti biçiminde tanımlanmıştır.
Örneğin bitlerden birisi yazma işleminin hex sistemde yapılıp yapılmayacağını
belirler. O bitin set edilip eski hale getirilmesi şöyle yapılabilir.
#include
<iostream.h>
void main(void)
{
long x;
x =
cout.flags();
cout.flags(x | ios::hex); /*hex
biçimde yazılmasını sağlar*/
cout.flags(x & ~ios::hex); /*hex biçimde yazılmamasını
sağlar*/
}
x_flags değişkeninin uygun
bitleri 1 yapılarak yazdırma işlemi çeşitli biçimlere çekilebilir.
/*-----cout2.cpp-----*/
#include
<iostream.h>
void main(void)
{
int a = 100;
long x;
cout << a <<
'\n';
x = cout.flags();
cout.flags(x & ~ios::hex
| ios::hex);
cout << a <<
'\n';
}
/*--------------------*/
Burada yapılan işlemi tek
aşamada yapan setf isimli bir üye fonksiyon da vardır.
long setf(long);
long unsetf(long);
setf fonksiyonu önce
x_flags içerisindeki değeri alır, bunu parametresiyle or işlemine sokarak
işlemi bir hamlede gerçekleştirir.
/*-----cout3.cpp-----*/
#include
<iostream.h>
void main(void)
{
int a = 100;
cout << a <<
'\n';
cout.setf(ios::hex);
cout << a <<
'\n';
cout.unsetf(ios::hex);
cout << a <<
'\n';
}
/*--------------------*/
ios sınıfının protected
x_width elemanı yazma işlemi için kaç karakter alanı kullanılacağını
belirlemekte kullanılır. Bu elemanla ilişki kuran iki fonksiyon vardır.
int width(void);
int width(int w);
Benzer biçimde x_precision
noktadan sonra kaç basamak yazılacağını belirlemekte kullanılır.
/*-----cout4.cpp-----*/
#include
<iostream.h>
void main(void)
{
double x = 3.52534;
cout << x <<
'\n';
cout.precision(10);
cout << x <<
'\n';
}
/*--------------------*/
Template
Fonksiyonlar
Genel
biçim:
template
<class(typename) T> [geri dönüş değeri] <fonksiyon
ismi>([parametreler])
{
}
Örnek:
template <class T>
void fonk(T a)
{
}
template bir anahtar
sözcüktür. template anahtar sözcüğünden sonra < > gelmelidir. < >
arasındaki class anahtar sözcüğünün normal sınıf işlemleriyle hiçbir ilgilisi
yoktur. class anahtra sözcüğü yerine 1996 standardizasyonunda typename anahtar
sözcüğü de kullanılabilir. class anahtar sözcüğünden sonra isimlendirme
kuralına göre herhangi bir isim yazılır. Bu isim bir tür belirtir. Template
fonksiyonlar ve sınıflar birer şablondur. Yani kendi başlarına kodda yer
kaplamazlar. Bir template fonksiyon çağırıldığında derleyici önce çağırılma
ifadesindeki parametre yapısını inceler. Bu türe uygun olarak şablonda
belirtilen fonksiyondan programcı için yazar.
/*-----templat.cpp-----*/
#include
<iostream.h>
template <class T>
void fonk(T a)
{
cout << a <<
endl;
}
void main(void)
{
fonk(3.5);
fonk(30L);
fonk(40L);
}
/*----------------------*/
Template fonksiyonlarda
tür belirten sözcüğe template argümanı denir. Template argümanının template
fonksiyon parametre yapısı içerisinde gözükmesi zorunludur. Zaten gözükmemesi
durumu da anlamsız olur. Bu template argümanı geri dönüş değeri olarak ya da
fonksiyon içerisinde tür belirten sözcük olarak kullanılabilir.
/*-----templat2.cpp-----*/
#include
<iostream.h>
template <class T>
T abs(T a)
{
return a > 0 ? a : -a;
}
void main(void)
{
cout << abs(-3.5)
<< endl;
cout << abs(-3)
<< endl;
cout << abs(-10L)
<< endl;
}
/*------------------------*/
Bir template fonksiyon
birden faazla farklı template argümanına sahip olabilir. Bu durumda pek çok
kombinasyon söz konusu olabilir.
/*-----templat3.cpp-----*/
#include
<iostream.h>
template <class A,
class B>
void fonk(A a, B b)
{
cout << a <<
endl << b << endl;
}
void main(void)
{
fonk(20L, 100);
fonk(100, 20L);
fonk(1.3, 10L);
fonk('c', 12);
}
/*------------------------*/
Template fonksiyonlar
çağırıldığında uygun parametre yapısı bulunamazsa error durumu oluşabilir.
/*-----templat4.cpp-----*/
#include
<iostream.h>
template <class T>
void swap(T *a, T *b)
{
T temp;
temp = *a;
*a = *b;
*b = temp;
}
void main(void)
{
int x = 10, y = 20;
swap(&x, &y);
cout << "x =
" << x << " " << "y = " << y
<< endl;
}
/*------------------------*/
Bir template fonksiyon
içerisinde başka bir template fonksiyon çağırılmış olabilir. Template argümanı
bir sınıf da olabilir. Bir template fonksiyonuyla aynı isimli normal bir
fonksiyon bir arada bulunabilir. Bu durumda iki anlamlılık hatası oluşmaz.
Normal fonksiyonun template fonksiyona karşı bir üstünlüğü vardır.
Sınıf Çalışması:
template <class T>
void sort(T *p, int size);
Biçiminde sort işlemi
yapan bir template fonksiyonu yazınız.
/*-----templat5.cpp-----*/
#include
<iostream.h>
template <class T>
void swap(T *a, T *b)
{
T temp;
temp = *a;
*a = *b;
*b = temp;
}
template <class T>
void sort(T *p, int size)
{
int i = 0, j = 0;
for (i = 0; i < size -
1; ++i)
for (j = i + 1; j <
size; ++j)
if (p[i] > p[j])
swap(p + i, p + j);
}
void main(void)
{
int a[5] = {3, 8, 4, 7,
6};
int i;
sort(a, 5);
for(i = 0; i < 5; ++i)
cout << a[i]
<< endl;
}
/*-----------------------*/
Template fonksiyonlar bir
şablon belirtir ve kütüphaneye yerleştirilemez. Bu yüzden header dosyalarının
içerisine yazılmalıdır.
Template Sınıflar
Template sınıflarda derleyici nesne tanımlama biçimine uygun bir sınıfın üye
fonksiyonlarının hepsini yazar. Genel biçimi:
template <class T>
class X {
};
Bu template argümanı sınıf
bildiriminin içerisinde ve bütün üye fonksiyonların içerisinde tür belirten
sözcük olarak kullanılabilir. Uygulamada ağırlıklı bir biçimde template
sınıflar kullanılmaktadır. Bir tenplate sınıfa ilişkin üye fonksiyon sınıfın
dışında aşağıdaki syntax biçimine uygun yazılmak zorundadır.
template <class T>
[geri_dönüş_değeri]
sınıf_ismi<T>::fonksiyon_ismi(parametreler)
{
}
Örnek:
template <class T>
void
X<T>::fonk(void)
{
}
Bir template sınıfa
ilişkin sınıf nesnesi şöyle tanımlanır:
sınıf_ismi <tür
ismi> nesne_ismi;
Örnek: Array <int>
x;
/*-----templat6.cpp-----*/
#include <stdio.h>
#include <stdlib.h>
template <class X>
class Array {
private:
int size;
X *p;
public:
Array(void);
Array(int n);
X & operator [](int
n);
};
template <class X>
Array<X>::Array(void)
{
size = 10;
p = new X[size];
}
template <class X>
Array<X>::Array(int
n)
{
size = n;
p = new X[size];
}
template <class X>
X &
Array<X>::operator[](int n)
{
if (n >= size) {
printf("Out of
range\n");
exit(1);
}
return p[n];
}
void main(void)
{
Array <float> n(5);
for (int k = 0; k < 5;
++k)
n[k] = k;
for (k = 0; k < 10;
++k)
printf("%f\n",
n[k]);
}
/*---------------------*/
Stack Sistemine İlişkin Bir template
Sınıf Örneği
Stack Sistemi Nedir?
Stack lifo sistemi ile çalışan bir kuyruk yapısıdır. Stack
sisteminin ismine stack pointer denilen bir göstericisi vardır. Bu gösterici o
anki stack pozisyonunu tutar. Stack sistemi ile ilgili iki temel işlem vardır:
1. Eleman Eklenmesi: Eleman eklenmesi durumunda önce stack gösterişcicsi
azaltılır daha sonra stck göstericisinin gösterdiği yere eleman yerleştirilir.
stack sistemini başlangıç konumunda sp tahsis edilmiş alanın en aşşasını
gösterir. Stack'e eleman eklenmesine push denir. Stack sistemine fazla eleman
yerleştirilmesi taşmaya yol açabilir. Bu işleme stack sisteminin üsten taşması
denir(stack overflow)
2. Eleman Çekilmesi: Bu durumda stack göstericisinin gösterdiyi yerden eleman
çekilir ve stack göstericisi bir artırılır. Stack'ten bilgi alınmasına pop
işlemi denir. Eğer fazla sayıda eleman çekilme işlemi uygulanırsa stack
aşşağıya doğru taşar. Bu işleme stack underflow denir.
Stack Sisteminin Uygulama Alanları
1. Ters düz etme işlemi. Bir yazıyı ters düz etmek için karakterler tek tek
stack sistemine eklenir ve sonra çekilir.
2. Undo işlemleri: Stasck sisteminin her elemanı yapılan işlem hakkında bilgiyi
tutan bir yapı şeklindedir. İşlemler yapıldıkça push işlemi ile stack sistemine
yerleştirilir. daha sonra undo yapıldıkça pop işlemi ile geri alınır.
3. Aktif pencerenin bulunması işlemi: Üstüste açılan pencerelerde son açılan
aktif penceredir o pencere kapatılırsa bir önceki pencere aktif pencere olur.
/*-----stack.h-----*/
/*-----stack.cpp-----*/
nesne tanımlanırken kullanılan template türü başka bir template sınıfı
olabilir. X <Y<int>> Z;
Çoklu
Türetme (multiple inherritence)
Bir sınıfın birden fazla
taban sınıfa sahip olması durumudur. Çoklu türetme durumunda başlangıç
fonksiyonlarının çağrılması bildirimde ilk yazılan taban sınıfın başlangıç
fonksiyonu önce çağrılacak biçimdedir. Örneğin class C:public A, public B {
böyle bir bildirimde önce a, b, c şeklinde başlangıç fonksiyonları çağrılır.
Bitiş fonksiyonlarıda c, b, a olacak şekilde tam tersdir.
Türemiş sınıf nesnesi
bütün taban sınıf nesnelerinide içerir. Taban sınıf nesnelerinin tüenmiş sınıf
içindeki organizasyonu standart olarak belirlenmemiştir. Ancak bildirimde
genllikle bildirimde ilk yazılan taban sınıfın vweri elemanları düşük anlamlı
adreste oacak biçimde ardışıl bir yerleşim söz konusudur.
/*-----multinhe.cpp-----*/
Çoklu Türetmede Faaliyet
Alanı ve İsim Arama
Çoklu
türetme urumunda türemiş sınıf bütün taban sınıflara erişebilir. taban
sınıflara erişim konusunda hiç bir önceklik yoktur. Bütün taban sınıflar aynı
faaliyet alanı içinde kabul edilir. Yani her iki taban sınıftada aynı isimli
bir veri elemanı yada üye fonksiyonu varsa ve erişim çözünürlük operatörü ile
yapılmamışsa bu durum iki anlamlılık hatasının oluşmasına yol açar.
int x;
int x;
 

C n;
n.x = 10; /*Bu işlem iki
anlamlılık hatasıdır*/
Yani türemiş sınıf üye
fonksiyonu içinde bir isim kukllşanıldığında bu isim:
1. Türemiş sınıf faaliyet
alanı içinde
2. Taban sınıfların her
birinin faaliyet alanı içinde aranır ve bir sıra gözetilmez.
Çoklu Türetmede Türemiş
Sınıf Adresinin Taban Sınıf Adresine Atanması Durumu:
Mademki
türemiş sınıfı nesnesinin adesi herhangibir tabna sınıf nesnesinin adresine
atana biliyor o halde adresteki işlemler geçerlidir.
 
 

C
x;
A *pa;
B *pb;
pa = &x;
pb = &x;/*işlemleri
geçerlidir*/
Çoklu
türetme durumunda türemiş sınıf nesnesinin adresi, herhangi bir taban sınıf
göstericisine atandığında derleyici türemiş sınıf nesnesinin taban sınıf veri
elemanının adresini göstericiye atar.
Çeşitli Çoklu Türetme
Şemaları
1. Bir sınıfın birden
fazla doğrudan taban sınıfı olamaz. Başka bir deyimle bir sınıf başka bir
sınıfa birden fazla taban sınıflık yapamaz.

 

/*Bu işlem olamaz.*/
class B: public A, public
A {
};
Böyle bir bildirimde aynı
sınıfın veri elemanlarından iki kez oluşturulur. Faaliyet alanı gereği aynı
isimli iki taban sınıf veri elemanlarına çatışma çözülerek erişim sağlanamaz.
|