C++ Dersleri: Sınıflar ve Yapılar 2
Programlamanın temellerinden olan C++ üzerine başlattığımız makalelere kısa bir aradan sonra yeni bir bölüm ile devam ediyoruz.
Bu makalemizde "Sınıflar ve Yapılar " konusunun ikinci bölümünü ele alacağız. Kısa süre sonra Yapıcılar(Constructor)Ve Yıkıcılar(Deconstructor) konusuna başlayacağız.
Sınıflara Değil, Nesnelere Değer Girmek
Bir insanın yaşı ve ağırlığı ne kadardır? Bu soruya gerçek hayatta cevap aranırsa kesin bir yanıt bulunamaz. Ancak değer aralıkları ve ihtimallerle karşılaşılır. Aynı şekilde C++’da da ‘Insan’ sınıfı kullanılarak bu sınıfın üye değişkenlerine değerler atanamaz. Ancak bu sınıfla tanımlanan bir nesneye, örneğin ‘Ali’ye bağlı olarak bir yaş ve kilo değeri belirlenebilir.
İnsan’ın yaşı = 20 (Yanlış)
Ali’nin yaşı = 20 (Doğru)
Bu eşitlikleri C++’a çevirecek olursak.
Insan.yas = 20; // Yanlış
Ali.yas = 20; // Doğru
‘Insan.yas’ı bir değere eşitlemeye çalışmak ‘int’i bir değere eşitlemeye çalışmaktan çok daha mantıklı değildir. Sırasıyla önce nesne tanımlanmalı, daha sonra bu nesne kullanılarak bir değer verilmelidir.
Insan Ali;
Ali.yas = 20;
Tanımlanmayan Özelliklerin Sınıfa Dahil Olmaması
Sınıf içerisinde tanımlanmayan bir özelliğin sınıfa dahil olmaması pek de şaşırılacak bir durum değildir. Ancak özellikle eski derleyicilerde sınıfların üye değişken ve fonksiyonlarını listeleme özelliği bulunmadığı için gerek yazım hatası, gerek dikkatsizlik sonucu tanımlanmayan bazı sınıf üyeleri kullanılmaya çalışılabilir ve sonucunda program hata verir.
Şimdi, aşağıdaki örneği inceleyelim:
class Kopek
{
int saldirganlikDerecesi;
int boy;
int agirlik;
OyunOyna( );
};
Kopek Arsiz;
Arsiz.saldirganlikDerecesi = 3;
Arsiz.yas = 4; // Yanlış
Arsiz.agirlik( );
Arsiz.Konus( ); // Yanlış
Burada, ‘Kopek’ isimli bir sınıf yapısı içerisinde, köpekler çeşitli özellikleri ile tanımlanmıştır. Ancak, sınıf içerisinde ‘yas’ tanımlanmadığı için ‘Arsiz.yas’ yanlış olur ve program hata verir. Aynı şekilde ‘Arsiz.Konus’ da yanlış bir ifade olacaktır.
Kullanıma Kapalı ve Açık Üyeler
Bu bölüm içerisinde şu ana kadar verilen program parçalarının her biri temsilidir ve çalışmaz haldedirler. Çünkü aksi söylenmediği sürece sınıf üyeleri kullanıma kapalıdır ve ancak sınıf fonksiyonları tarafından erişilebilirler.
Üyelerin erişilebilir veya erişilemez olmasını belirleyen komutlar ‘public’ ve ‘private’dır yani ‘açık’ ve ‘özel’. Eğer hiçbir tanımlama yapılmazsa sınıfın üyeleri otomatik olarak ‘private’ kabul edilir. Bu yüzden üyelere erişmek için ‘public’ tanımlaması yapılması gerekir. ‘public’ ve ‘private’ birer label gibi tanımlanırlar yani sonlarına ‘:’ işareti konulur.
class SinifIsmi( )
{
public:
…
…
private:
…
…
};
Açık Üyelere Erişim
Bir sınıfın üyelerine doğrudan erişmek isteniyorsa bu üyeleri ‘public’ kısımda tanımlamak gerekir.
class Kedi( )
{
public:
unsigned short int yas;
Miyavla( );
};
Kedi Tekir;
Tekir.yas = 2; // Doğru
Tekir.Miyavla( ); // Doğru
Bu şekilde sınıfın üyelerine ulaşılabilir.
Üye Verilerini Kapalı Yapmak
Programcının karşısına zaman zaman öyle durumlar çıkar ki bir üye değişkenin yalnızca bir üye fonksiyon tarafından çağırılması tercih edilebilir. Örneğin geriye sayan bir saatli bombaya müdahale edilmesi istenmiyorsa sayaç gizli bir değişken yani ‘private’ olarak tanımlanır ve ‘public’ kısımda tanımlanmış GeriSay() isimli bir üye fonksiyon kullanılarak sayaç sürekli olarak azaltılır. Eğer sayaca erişebilen başka bir üye fonksiyon yok ise patlama kaçınılmazdır. Şimdi bu anlatılanları programa dökelim:
#include <iostream.h>
#include <conio.h>
#include <dos.h>
class SaatliBomba
{
public:
void SayaciAyarla(int zaman);
void GeriSay();
int SayaciOku();
private:
unsigned short int sayac;
};
void SaatliBomba::SayaciAyarla(int zaman)
{
sayac = zaman;
}
void SaatliBomba::GeriSay()
{
sayac–;
}
int SaatliBomba::SayaciOku()
{
return sayac;
}
void main()
{
SaatliBomba TNT;
TNT.SayaciAyarla(10);
while(TNT.SayaciOku() >= 0)
{
clrscr();
cout<<"Sayac = "<<TNT.SayaciOku();
delay(1000);
TNT.GeriSay();
}
clrscr();
cout<<"BOOOOM!!!\n";
getch();
}
İlk bakışta program bir parça karışık gözükebilir. Bu karmaşayı üretan en önemli faktör “void SaatliBomba::SayaciAyarla(int zaman)” gibi fonksiyon tanımlamalarıdır. Eğer bir sınıfa ait bir üye fonksiyon tanımlanacaksa, fonksiyon isminden önce sınıfın ismi yazılır ve yan yana iki tane ‘:’ işareti konulduktan sonra fonksiyonun adı yazılır.
Şu ana kadar kullanmadığımız ‘dos.h’ kütüphanesinin bu programda kullanılmasının nedeni ‘delay’ komutudur. ‘delay’ komutu kullanımı çok basit olan bir komuttur. Tek parametreye sahiptir ve hiçbir değer geri döndürmez (void tipinde bir fonksiyondur). Görevi parantez içerisinde milisaniye cinsinden verilen değer kadar beklemektir.
1 sn = 1000 ms
Bu sebepten dolayı bir sonraki komut satırı çalıştırılmadan önce bilgisayarın 1 sn. beklemesi istendiğinde, programa aşağıdaki gibi bir satır eklenir:
delay(1000);
Şimdi adım adım programı açıklayalım:
Programın başında gerekli kütüphaneler tanımlanıyor. Daha sonra ‘SaatliBomba’ adında bir sınıf üretılıyor. Bu sınıf iki bölümden oluşmuş, ‘public’ ve ‘private’. ‘public’ kısmında SayaciAyarla( ), GeriSay( ) ve SayaciOku( ) fonksiyonları tanımlanıyor. ‘sayac’ ‘private’ bölümünde yer aldığı ve doğrudan ulaşılamadığı için SayaciOku( ) gibi bir fonksiyona ihtiyaç duyuyoruz.
‘SaatliBomba’ sınıfının tanımlaması bittikten sonra, sıra bu sınıfa ait üye fonksiyonların tanımlamalarına geliyor. Prototipleri sınıfın içerisinde tanımlanmış olan fonksiyonlar tipik fonksiyon tanımlaması şeklinde tanımlanıyor. Ancak tek fark bu fonksiyonların bir sınıfa ait hatta ‘SaatliBomba’ sınıfına ait olduğunu belirtmek için fonksiyon isminden önce ‘SaatliBomba’ yazılıyor ve iki ismi ayırmak amacıyla araya ‘::’ konuluyor.
Üç üye fonksiyon tanımlandıktan sonra ana fonksiyona geliniyor. Burada TNT isimli bir saatli bomba tanımlanıyor ve sayacı 10 sn’ye ayarlanıyor. Bunu yapan fonksiyon “SayaciAyarla(int zaman)” fonksiyonudur. Eğer ‘sayac’ public olsaydı bu fonksiyona gerek kalmaksızın “TNT.sayac = 10;” demek mümkün olacaktı. Ancak ‘sayac’ ‘private’ bir değişken olduğu için ancak aynı sınıfa ait bir üye fonksiyon içerisinden erişilebiliyor.
TNT adlı bombanın sayaç ayarını yaptıktan sonra SayaciOku( ) fonksiyonuyla okunan değer 0’a eşit veya 0’dan büyük olduğu sürece ekran siliniyor, SayaciOku( ) kullanılarak sayacın değeri ekrana yazılıyor, ‘delay’ ile 1 sn bekletiliyor ve GeriSay( ) ile sayacın değeri bir azaltılıyor.
Değer 0’ın altına düştüğü anda ‘while’ döngüsü sona eriyor ve ekran silinerek ‘BOOOOM!!!’ mesajı ekrana yazılıyor, yani bomba patlıyor. Bir tuşa basılmasını bekleyen program bir tuşa basıldığı anda sona eriyor.
Sınıfın Bir Üyesini Başka Bir Sınıfla Tanımlamak
Daha önce de bahsettiğimiz gibi bir sınıfın üye değişkeni başka bir sınıfla tanımlanmış olabilir. Sınıf içi sınıf kavramının daha iyi anlaşılması için biraz teorik ancak açıklayıcı bir örneği inceleyelim:
#include <stdio.h>
#include <conio.h>
#include <iostream.h>
class A
{
public:
int x;
};
class B
{
public:
A y;
};
void main( )
{
clrscr( );
B Nesne;
Nesne.y.x = 3;
Cout<<"Nesnenin ilk degeri = "<< Nesne.y.x<<endl;
Nesne.y.x++;
Cout<<"Nesnenin ikinci degeri = "<< Nesne.y.x;
getch( );
}
Bu örnekte A sınıfında bir ‘x’ tamsayı değişkeni tanımlandıktan sonra B sınıfında bir ‘y’ A tipi değişken tanımlanıyor. Daha sonra ana fonksiyonda B tipi bir nesne tanımlanıyor ve bu nesnenin üye değişkeni olan ‘y’ ye değer girilmek isteniyor. Ancak ‘y’ bir tamsayı değişken değil, A tipinden bir değişken. A sınıfında tamsayı olan ‘x’ tipi bir değişken tanımlı. Bu durumda A tipiyle tanımlanmış olan ‘y’ nin üye değişkeni olan ‘x’ e ulaşmak üzere ‘y.x’ yazmak gerekiyor. Sonuçta ‘Nesne’ ye bir tamsayı değer vermek gerektiğinde ‘Nesne.y.x’ şeklinde sınıf içi sınıf kullanılmış oluyor.
Bu örnek her ne kadar teorik olması açısından karmaşık görünse de, başka programcılar tarafından yazılmış bu tür anlamsız görünen program parçalarını inceleyip anlamak yönünden iyi bir pratik olacaktır.
Struct Komutu ile Class Komutu Arasındaki İlişki
Bu bölüme ‘Sınıflar ve Yapılar’ başlığı verdik Ancak şu ana kadar hep sınıflardan bahsettik. Çünkü sınıf ve yapı hemen hemen aynı şeydir. Biri ‘class’ diğeri ise ‘struct’ komutu ile tanımlanır. Aralarındaki asıl fark ise yapı (structure) ‘private’ bölümünü içermez, yalnızca ‘public’den ibarettir. Ayrıca yapılarda ‘public:’ şeklinde bir tanımlamaya da ihtiyaç duyulmaz ve doğrudan küme parantezinden sonra ilgili üyeler girilir.
Yapıların sınıflara göre eksikliği sadece fonksiyonlar tarafından erişilebilen gizli üye değişkenler üretılamamasıdır. Bütün üye değişkenlere nesne üzerinden direkt erişim mümkündür. Bunun yanında, bu tür gizli üye değişkenler kullanılmayacağı takdirde sınıflarda mecburen yaptığımız ‘public:’ tanımlamasını yapmadan yapıları kullanabiliriz.
Faruk Alkaya
Selçuk Üniversitesi - Teknik Eğitim Fakültesi
KAYNAKLAR:
- Sevinç Gülseçen & Emir Akaydın C++ DERS NOTLARI
- www.programlama.com (c++ dökümanları)
- Bora Güngören – C++ ile Nesne Tabanlı Programlama ISBN:975 347 785 6
- www.yesevi.net uzaktan eğitim sitesi
- 08 Aralık 2006
Yorum Yapın