Bilgi/Açıklama

Kalıtım (Inheritance)

Kalıtım Kavramı

Bir sınıfın, diğer bir sınıftaki alan ve işlevlerini almasına kalıtım (inheritance) denir. Bu şekilde birden çok sınıfın ortak özellikleri bir taban sınıfta toplanır ve bu sınıftan, daha özel durumlar için kullanılan sınıflar türetilir. Örneğin 'bilgisayar' bir taban sınıfı 'masaüstü' ve 'dizüstü' ondan türetilmiş sınıflar olabilir. Bir sınıfın türetildiği sınıfa ata-ana (parent), taban (base) veya üst (super) sınıf gibi isimler verilirken; türetilen sınıf için çocuk (child), türemiş (derived) veya alt (sub) gibi adlar kullanılır. Bir sınıfın türetildiği sınıf da başka bir sınıftan türetilmiş olabilir. Örneğin 'masaüstü' ve 'dizüstü' bilgisayarların türetildiği 'bilgisayar' sınıfı da elektronik 'aygıt' sınıfından türetilmiş olabilir.
Kalıtım özelliğinin C++ dilinde bulunmasının en önemli nedeni; birbirine benzeyen, ortak noktaları çok olan bir çok sınıfta, aynı alanların ve yöntemlerin tekrar tekrar tanımlanmasından kurtulunması ve daha az kod yazarak yazılım geliştirilebilmesine olanak sağlanmasıdır. Ancak kalıtım yalnızca daha az kod yazmak için ortaya atılmış bir kavram değildir. Bir kod parçası, sınıfların ortak özelliklerini tanımlandığı bir üst sınıf için yazılır ve daha sonra üst sınıftan türeyen alt sınıfların, düzende kendinden olarak tanımlanması sağlanır. Örneğin bir bilgisayarı açan, bir işlem yapan ve sonra da kapatan bir süreç, bilgisayar için yazıldığında masaüstü için de dizüstü için de geçerli olur. Daha sonra 'avuç içi' diye bir sınıf bilgisayar sınıfından türetilirse aynı işlemler onun için de geçerli olur.
Bir veya daha fazla sınıfın türetildiği, ancak tek başına somut bir anlam içermeyen sınıflara soyut (abstract) sınıflar denir. Üst sınıflarda tanımlanan işlevler alt sınıfta yeniden tanımlanabilir. Buna ezme (override) adı verilir. Üst sınıfın, ezilmesine olanak verilen yöntemlerine sanal (virtual) denir. Soyut sınıfların bazı yöntemleri bildirilip tanımlanmayabilir. Yani üst sınıfta bir yöntemin yalnızca adı, giriş parametreleri ve dönüş tipi bildirilir. Alt sınıflarda bu yöntemlerin içi, yani kod bloğu yazılır. Üst sınıfta yalnızca bildirimi olan, alt sınıfların içini doldurduğu işlevlere arı sanal (pure virtual) işlevler denir. Kalıtımla birlikte gelen bir başka konu da çok biçimlilik (polymorphism) kavramıdır. Çok biçimlilik, bir işlevin üst sınıfta aynı biçimde bildirilip alt sınıflarda farklı biçimlerde gerçekleştirilmesi ve bu sınıfları kullanan kodların her sınıfın ayrı gerçekleştirmeleri ile ilgilenmeden standart olarak, aynı biçimde erişmeleridir.

Kaplama

Kaplama Sözcükleri

Bir sınıfın üye değişken ve işlevleri ya özel (private), ya kamusal (public) ya de korumalı (protected) olabilir. Bir sınıftaki özel üyeler yalnızca o sınıf tarafından erişilir, kamusal üyeler her yerden erişilebilir. Bir sınıfın korumalı üyeleri ise sınıfın içinden ve o sınıftan türetilen sınıflar tarafından erişilebilir.

Özel ve Kamusal Kalıtım

Bir sınıfın diğerinden türetilmesi durumunda üst sınıfın özel, korumalı ve kamusal üyelerine alt sınıf için özel mi korumalı mı kamusal mı olacağını belirlemek için türetmenin özel ve kamusal olması söz konusudur. Bir sınıfın diğerinden türetilmesi özel (private) olarak yapılırsa üst sınıfın üyelere özel de olsalar, korumalı veya kamusal da olsalar alt sınıf için özel kabul edilir. Eğer türetme kamusal (public) yapılırsa üst sınıfta gizleme düzeyi neyse alt sınıfta da o olur. Yani üst sınıfta özel olan alt sınıfta da özeldir, korumalı olan alt sınıfta da korumalıdır, kamusal olan alt sınıfta da kamusaldır.

Türetme (Derivation)

Bir sınıf diğer bir sınıftan türetildiği zaman, başka bir deyişle sınıflar arasında türetme (derivation) ilişkisi olduğunda, o sınıfın bütün üyelerine sahip olur. Örnek olarak 'kuş' (bird) diye bir üst sınıf ve bu sınıftan türetilmiş 'güvercin' diye bir sınıf yapılmak istenirse, ismini gösteren bir alan ve uçmasını sağlayan bir yöntem içeren kuş sınıfı
class bird{
public:
	char *name;
	void fly(){
		cout<<name<<" uçuyor"<<endl;
	}
};
biçiminde yazılabilir. Bundan kamusal olarak türetilen bir güvercin sınıfı
class pigeon : public bird{
	
};
biçiminde yazılabilir. Burada pigeon sınıfı bird sınıfından kamusal olarak türetilmiştir, yani ondaki bütün üyeleri almaktadır. Bu pigeon sınıfını kullanırken kendisinden tanımlı olmayan, üst sınıfı bird sınıfından aldığı alan ve işlevleri kullanılabilir:
pigeon p;
p.name="Ak Güvercin";
p.fly();
Görüldüğü gibi name alanı ve fly() yöntemi, pigeon sınıfında tanımlı olmadığı halde, bird sınıfından türetildiği için pigeon sınıfının üyeleri olarak kullanılabilmektedir. Bir sınıfa ait bir alan veya yöntem kullanıldığı zaman derleyici öncellikle üyeyi belirtilen sınıfta arar; orada bulamazsa türetildiği bir üst sınıfta arar, orada da bulamazsa daha üstünde, nihayet en üstteki sınıfta arar. Burada kalıtım kamusal olmayıp özel olsaydı, pigeon sınıfı içinde herhangi bir yöntemde kalıtılan üyeler kullanılabilirdi ancak dışarıdan bir kod parçası bunlara erişemezdi.
Bir sınıftan bir çok sınıf türetilebilir. Örneğin bird sınıfından pigeon dışında bir falcon (doğan) sınıfı da türetilebilir :
class falcon : public bird{
};
Buradaki falcon sınıfı da üst sınıfı bird sınıfının üyelerine sahip olmuş bulunmaktadır:
falcon f;
f.name="Kara Doğan";
f.fly();

Ezme(Overriding)

Bir sınıf, türetildiği sınıftan aldığı bazı yöntemlere aynen sahip olabilir. Ancak bazı durumlarda alt sınıf üst sınıfın bir yöntemini yeniden yazması gerekebilir. Buna ezme (override) denir. Örneğin pigeon sınıfı türetildiği bird sınıfındaki fly() yöntemini
class pigeon : public bird{
public :
	void fly(){
		cout<<name<<" uçuyor"<<endl;
		cout<<" güzelce"<<endl;
	}		
};
biçiminde ezebilir. Bu durumda bu yöntemi çağıran kodda herhangi bir değişiklik olmaz. Yani
pigeon p;
p.name="Ak Güvercin";
p.fly();
şeklindeki kop parçasında fly() yöntemi, ezme olmaması durumunda bird sınıfındaki fly() yöntemini çağırırken, ezme durumunda pigeon sınıfındaki fly() yöntemini çağırır.

Ezmede Üst Sınıfın Yöntemini Kullanma

Bazı durumlarda alt sınıf üstündeki yöntemi ezmek isterken üst sınıfın yönteminin de çalışmasını isteyebilir. Bu durum alt sınıfın üst sınıftaki yöntem çağrılmadan bazı denetimler yapması veya alt sınıfın üst sınıftaki işleve bazı eklemeler yapması hallerinde ortaya çıkar. Örneğin falcon sınıfı bird sınıfının fly() yöntemini hem ezer hem de kullanırsa
class falcon : public bird{
public :
	void fly(){
		bird::fly();
		cout<<"  yüksekte"<<endl;
	}	
};
biçiminde bir kod yazılır. Burada üst sınıfın yönteminden bahsedildiğini göstermek için bird::fly() denmektedir. Eğer işlevin başındaki sınıf konmazsa
void fly(){
	fly();
	cout<<" yüksekte"<<endl;
}
şeklinde bir kod yazılır, bu da üst sınıfın yöntemini değil kendisini, (recursive) bir biçimde yineleyen çağırır. Derlerken sorun çıkmaz ama kod çalışırken sonsuza kadar kendisini çağırmaya çalışır ve en sonunda bellek yetersizliği nedeniyle program kilitlenir. Kimi derleyiciler bu sorunu anlayıp yanlışlık bildirimi yaparlar.

Alt Sınıfla Üst Sınıf Bildirimi

Bir alt sınıf aynı zamanda üst sınıfın bütün özelliklerini taşıdığı için, alt sınıfa ait bir örneğe tür olarak üst sınıf bildirilebilir. Örneğin
pigeon p;
bird b=p;
b.name="Taban Güvercin";
b.fly();
Bu sınıflardan pigeon sınıfı bird sınıfından türediği için derleyici açısından bir hata oluşmaz. Ancak bu durumda fly() işlevi bird sınıfındaki işlevi çağırır, pigeon sınıfındakini değil.

Kalıtımda Göstericiler (Pointers in Inheritance)

C++ dilinde gösterici (pointer) özelliği sınıflar için kullanıldığında kalıtım kuralları geçerlidir. Yani bir sınıfa ait göstericiyle üst sınıftan alınan üyelere erişim yapılabilir. Örneğin pigeon sınıfına ait bir gösterici
pigeon *pp;
pp=new pigeon();
pp->name="Gösterici Güvercin";
pp->fly();
biçiminde kullanılabilir. Üst sınıflara ait göstericiler alt sınıfları göstermek için kullanılabilir. Örneğin
bird *bp;
bp=new pigeon();
bp->name="Taban Gösterici Güvercin";
bp->fly();
biçimindeki kod parçasında göstericinin tipi bird olarak bildiriliyor ancak örnek pigeon türünde oluşturuluyor.

Sanal Yıkıcı (Virtual Destructor)

Kurucular genellikle sanal bırakılmaz. Çünkü alt sınıfın kurucuları üst sınıfınkini çoktan ezmiş sayılır. Alt sınıfın kurucusu çağrıldığında üst sınıfın da kurucusu kendinden çağrılır. Öte yandan yıkıcıların sanal olarak tanımlanması çoğu kez gerekli olur. Çünkü tersi durumda alt sınıfın yıkıcısı çağrıldığında üst sınıfın yıkıcısı çağrılmaz. Üst sınıfta bir takım kaynakların salındığı, gerekli temizliklerin yapıldığı koşullarda bu durum bir sakınca oluşturabilir. Bu nedenle sanal yıkıcı (virtual destructor) tanımlamak yararlıdır. Örnek:
class BaseClass
{
public:
    BaseClass();
    virtual ~BaseClass();
}

Sanal (Virtual) İşlevler

Bir alt sınıf üst sınıfın bir işlevini ezdiği zaman alt sınıftan oluşan örneklerde ezen işlev geçerli hale gelir. Ancak üst sınıfa ait göstericiler alt sınıflar için kullanıldığında üst sınıftaki işlev geçerli olur. Bu durumu değiştirmek için, yani göstericinin türü alt sınıf olmasına rağmen çağrılan işlevin örnek oluşturulan türdeki alt sınıfa ait olmasını sağlamak için üst sınıfın işlevi virtual (sanal) olarak işaretlenir. Örneğin bird sınıfındaki fly() işlevi
class bird{
public:
	char *name;
	virtual void fly(){
		cout<<name<<" is flying";
	}
};
biçimindeki kodda sanal olarak işaretlenir. Alt sınıfta sanal işlevler için herhangi bir işlem yapmaya gerek yoktur, ezme işlemi olağan biçimde yapılabilir :
class pigeon : public bird{
public :
	void fly(){
		cout<<name<<" is flying nice "<<endl;
	}
};
Ancak bu durumda gösterici kullanılması durumunda çağrılan işlev alt sınıfa ait işlev olacaktır. Örneğin
bird *bp;
bp=new pigeon();
bp->name="Base Pointer Pigeon";
bp->fly();
şeklindeki kod parçasında fly() yöntemi gösterici bird türünde tanımlı dahi olsa örneğin fly() yöntemi çağrılır. Başka bir deyişle, bir işlevi virtual yapmak bütün durumlarda alt sınıfların ezme yapan işlevlerinin geçerli olduğunu belirtmek demektir.

Arı Sanal (Pure Virtual)

Arı Sanal (Pure Virtual) İşlevler

Bazı durumlarda taban sınıfta bir işlevin olması gerekir fakat işlevin içini oluşturan kod parçası her alt sınıf için değişik olduğundan üst sınıfta yazılamaz. Örneği 'balık' (fish) için 'yüzme' işlevi tanımlanmalıdır. Ancak diyelim ki her balık kendine özgü bir tarzda yüzmektedir ve hiçbir şekilde ortak bir kod yazılamaz. Bu durumda üst sınıfa swim() diye bir yöntem koymamak da doğru olmaz. Zira her balık faklı biçimlerde de olsa yüzer. Üst sınıfta bildirilen, ancak alt sınıfta gerçekleştiren kodun yazıldığı işlevler arı sanal (pure virtual) olarak nitelendirilir. Bunların işlev adı, giriş parametreleri ve dönüş tipi belirtilir ancak kod bloğu yazılmaz. Örneğin balık fish diye bir sınıf
class fish{
public :
	char *name;
	virtual void swim()=0;
};
biçiminde yazılır. Burada swim() diye bir yöntem bildirilmektedir ancak devamında kıvırcık ayraçlar ('{' ve '}') kullanarak kod yazılmaz, yerine boş olduğunu göstermek için '=0' yazılır ve noktalı virgül (';') ile ifade bildirim bitirilir. Bu sınıftan türeyen sınıflar fly() yöntemlerinin içini yazarlar. Örneğin hamsi (anchovy) ve uskumru (mackarel) sınıfları yapalım. Bunlardan anchovy sınıfı aşağıdaki gibi yapılabilir:
class anchovy : public fish{
public :
	void swim(){
		cout<<name<<" yüzüyor.."<<endl;
	}
};
Ve mackerel sınıfı aşağıdaki gibi yapılabilir:
class mackerel : public fish{
public :
	void swim(){
		cout<<name<<" yüzüyor.."<<endl;
	}
};

Soyut Sınıf (Abstract Class)

Bir veya daha fazla saf sanal işlevi bulunan sınıflara soyut (abstract) sınıf denir. Karşıtı olan somut (concrete) sınıfların aksine, bu sınıflar yalnızca türetilmek için yapılmışlardır. Soyut sınıflara ait örnekler oluşturulamaz. Örneğin
fish f;
biçiminde bir bildirim derleyici hatasına yol açar. Çünkü fish sınıfı soyuttur. Başka bir deyişle bir balık kesinlikle hamsi (anchovy), uskumru (mackerel) veya balık sınıfından türeyen somut bir sınıf olmalıdır.
Soyut bir sınıftan türeyen ancak arı sanal işlevlerini yazmayan sınıflar da soyut olur. Örneğin mackerel sınıfında swim() yöntemi yazılmazsa bunun anlamı bu sınıfında soyut olduğu, bu sınıftan türeyen başka bir sınıfın bu swim() yöntemini yazacağı anlamına gelir.

Çokbiçimlilik (Polymorphism)

Sanal işlevlerin kullanılmasıyla çok biçimlilik (polymorphism) kavramı ortaya çıkar. Bunun anlamı bir işleve dışarıdan aynı isimle ve parametrelerle erişilirken aslında içeride farklı bir kod parçasının çalışmasıdır. Örneğin fish sınıfından türetilmiş acnchovy ve mackerel sınıfılarına, göstericiler kullanılarak erişen bir kod parçası
fish *fp1;
fp1=new anchovy();
fp1->name="Black Sea Anchovy";
fp1->swim();
fish *fp2;
fp2=new mackerel();
fp2->name="Karadeniz Uskumrusu";
fp2->swim();
biçiminde yazılabilir. Burada iki ayrı sınıfa ait iki ayrı örnekle çalışıldığı halde fly() yöntem çağrısı aynı biçimde yapılmaktadır. Ancak iki örnekteki yöntem aynı biçimde çağrılsa da farklı işlevler, yani örnek hangi sınıftansa o sınıfta tanımlanmış işlevler çalışmaktadır.
Kalıtım, soyut sınıflar ve çok biçimlilik gibi konular sayesinde, bir yazılım düzenin üst sınıflar için tanımlanması ve daha sonra türetilecek sınıflar için altyapı oluşturulması olanaklı olur. Örneğin içinde belirtildiği kadar balık olan bir havuzu temsil eden pool (havuz) diye bir sınıf
class pool{
public :
	fish **fishes;
	int count;
	pool(int n){
		count=n;	
		fishes=new fish*[count];
	}
	void set_fish(int i,fish *f){
		fishes[i]=f;
	}
	fish *get_fish(int i){
		return fishes[i];
	}
	void swim_all(){
		cout<<"Havuzda yüzenler : "<<endl;
		for(int i=0;i<count;i++){
			get_fish(i)->swim();
		}		
	}
};
biçiminde yazılabilir. Burada fish sınıfına ait fishes adında göstericiler dizisi bildirilmekte ve kurucuda parametreyle aktarılan miktarda balık için yer ayrılmaktadır. Belli bir dizinindeki balığa erişmek için get_fish() ve set_fish() yöntemleri tanımlanmıştır. Havuzdaki bütün balıkların yüzmesi için de swim_all() yöntemi bulunmaktadır. Bu sınıf tamamen soyut olan fish sınıfına göre tanımlanmaktadır. Yani fish sınıfından türetilen herhangi bir sınıfa ait örnekler bu sınıfta kullanılabilir. Bu çeşit yazılım yapmanın yararı, daha sonra yazılacak sınıfların da bu tür çok biçimli sınıflarda kullanılabilir olmasıdır.
Yukarıdaki pool sınıfı aşağıdaki gibi kullanılabilir:
pool p(2);
p.set_fish(0,fp1);
p.set_fish(1,fp2);
p.swim_all();

Burada iki balıklık havuz tanımlanmakta, iki balık havuza konmakta ve yüzdürülmektedir.
tekzaf tarafından 23.09.2018 tarihinde eklenmiş/güncellenmiştir.

Bu Sayfayı Paylaş:

Fibiler Üyelerinin Yorumları


Tüm üyeler içeriklere yorum ekleyerek katkıda bulunabilir : Yorum Gir

Misafir Yorumları




Bu Sayfayı Paylaş:

İletişim/Bize Yazın   mh@fibiler.com   Google+   Facebook   Twitter   fibiler@googlegroups.com
Her Hakkı Saklıdır
Bu sitede yayınlanan tüm bilgi ve fikirlerin kullanımından fibiler.com sorumlu değildir.
Bu sitede üretilmiş , derlenmiş içerikleri, fibiler.com'u kaynak göstermek koşuluyla kendi sitenizde kullanılabilirsiniz. Ancak telif hakkı olan içeriklerin hakları sahiplerine aittir