2.2 Yorumlayıcılar (Interpreter)
Çoğu bilgisayarlar, derleyici yerine bir yorumlayıcıya (interpreter) sahip olmayı isterler. Daha önceleri bazı büyük bilgisayarlarda ve çok büyük bilgisayarlarda daha çok derleyiciler kullanılmaktaydı, diğer bilgisayarlarda daha çok yorumlayıcılar kullanılmaktaydı. Fakat günümüzde, PC tipi bilgisayarların hızının eski tip büyük bilgisayarların hızını geçmesi derleyicilerin bunlarda da kullanımını mümkün kılmaktadır.
Yorumlayıcı, bir programdaki satırı bir anda bir satır işlemiyle yorumlar. Yani satırlar tek tek yorumlanarak amaç programa erişilir. Bir satır başarılmadan sonraki satıra geçilmez. Yorumlayıcı, değişkenler kütüphanesini ve onların adreslerini derler ve çevrilmiş program satırının gerekli olduğu yerlere mutlak adresi sıkıştırır.
Yorumlayıcı tarafından ele alınmış bir program, derlenmiş bir programdan daha yavaş çalışır ve optimizasyon mümkün değildir. Bu özellik, hızlı etkileşimli uygulamalarda ve debug işlemlerinde faydalıdır. Bununla birlikte, yorumlanmış bir program, derlenmiş bir programdan daha kısa olmasına rağmen bellekte daha az yer kaplar. Bu faktör, sınırlı belleklerle çalışan ve genişletilmesi öngörülen bilgisayarların tasarımında önemli bir yer tutar. Benzer olarak hata ayıklamalarında yorumlayıcı, derleyiciden daha az başarılıdır.
Yorumlayıcı, her ifadeyi bellekte yerini alacak makine dili kodlarına çevirmez ve bundan dolayı da amaç program üretmez. Program satırlarının çevrilmesi sırasında üretilen makine kodu silinirken satır ifadeleri işletilir. Yorumlayıcıda, bir satırdaki hata düzeltilmeden diğer program satırına geçilmez. Bu durum aşağıdaki şekilde detaylı olarak gösterilmiştir.
Ayrıca yorumlayıcı, üst düzey komutları alarak her bir ifadeyi çalıştıracak ön tanımlamalı makine komutları dizisine referanslar. Bu dizi, sadece okunan veya gelişi güzel erişimli bellek tiplerinde depolanmalıdır. Bütün diziler ve bakış tabloları, RAM’deyken, yorumlama işi disk veya disketten RAM’de yazılım yüklemeli olarak yürütülür.
ROM
Çevirme
RAM RAM
Sonraki Satır
Şekil 2.3 Basit Bir Yorumlayıcının Yapısı
Yorumlayıcı, bir çok endüstriyel üretimde kullanıldığından, ROM’da monitör program (firmware) olarak tutulur. Üst düzey diller, genellikle derleyici kullanırken Basic gibi diller yorumlayıcı kullanırlar. Yorumlayıcının ifadeleri ve kaynak kodlarının makine diline tek tek çevrilmesi, etkileşimli kullanım için en uygun yoldur. Fakat benzer uygulamalarda, hata mesajlarını çözmek programcıya düşerken, ,iş de yavaşlar. Eğer hata mesajı kritik ise bu tip hatalar aşağıdaki örneğe yakın bir şekilde ortaya çıkar.
Syntax error 40, line 20 yani hatanın hangi satırda olduğunu belirtir ve kürsör gidip o satırın başında bekler. Yukarıdaki hataya bakılarak çevirme işi sırasında sonraki satırın yorumlanmasına geçmeden önce, satır numarası ve hata kodu verilen bu hata programcı tarafından giderilir. Hatanın olduğu kısma kadar çalıştığı kesindir.
Yorumlayıcı kullanıldığı zaman istenilen değişiklik program üzerinde yapılabilir ve program derlenmeden çalıştırılabiliyor. Bu yüzden program geliştirme esnasında çok kullanışlıdır. Turbo Pascal’da hem yorumlayıcı hem de derleyici mevcuttur.
2.3 Derleyici ile Yorumlayıcı Arasındaki Farklar
Yorumlayıcı da satırlar tek tek yorumlanarak amaç programa erişilir.
Mikroişlemci hızı düşük bilgisayarlarda, genelde yorumlayıcılar kullanılır.
Çalışma hızı yavaştır.
Bir satırdaki hata düzeltilmeden, diğer satırın yorumlanması işemi yapılmaz.
Yorumlanmış bir program, bellekte daha az yer işgal eder.
Satırlar tek tek yorumlanarak amaç programa erişilir.
Program geliştirmede çok kullanışlıdır.
Derleyici de satırlar bir anda yorumlanarak amaç programa erişim sağlanır. Tüm program bir anda derlenebilir.
Mikroişlemci hızı yüksek bilgisayarlarda daha çok derleyici kullanılır. (Günü-müz PC’lerinde işlemci hızı yüksek olduğundan, artık hem derleyici hem de yo-rumlayıcısı olan programlar kullanılmaktadır.)
Hata ayıklamalarında derleyici daha başarılıdır.
Derleme işi çok hızlı gerçekleşir.
Derlenmiş program, bellekte daha fazla yer kaplar.
Aşağıda, derlenmiş ve yorumlanmış program akışları arasındaki farkı anlatan algoritmalar verilmiştir.
Problem
Algoritma Geliştirme
Program
Bilgisayara Gir Düzeltme Yap
Evet
Hata Var Mı?
Hayır
Tamamla
Şekil 2.4 Yorumlanmış Program Akışı
Problem
Algoritma Geliştirme
Program Yaz
Bilgisayara Gir Düzeltme Yap
Derle
Evet
Hata Var Mı?
Hayır
Evet Hayır
Amaç Kodu Yükle Sentaks Hatası
Çalıştırmaya Başla
Evet
Hata Var Mı?
Hayır
Tamamla
Şekil 2.5 Derlenmiş Program Akışı
2.4 Derleyiciyi Oluşturan Unsurlar
SINIFLAR(classes)
Sınıflar nesne yönelimli programlama tekniğini uygulayabilmek için mutlaka gerekli olan C'deki yapılara benzeyen C++'a özgü veri yapılarıdır.
Tıpkı yapılarda olduğu gibi sınıflarla da çalışmadan önce bir sınıf bildirimi yapmak gerekir. Sınıf bildirimi bellekte yer kaplamaz(C++'ta nesne terimi daha çok bir sınıf türünden değişke ni anlatmakta kullanılır. Nesne yönelimli programlama tekniği sınıflar kullanılarak program yazma tekniğidir).
4.1 Sınıf Yapısı ve Nesneler
Sınıf, birbiriyle ilişkili verilerin ve bu verileri kullanmayı sağlayacak yordamların bir arada bulundurulmasını sağlayan bir yapıdır. Veri ve yordamların sistemin kalanından ayrılmasını ve gerekliyse gizlenmesini sağlar. Bir sınıf yaratmak yeni bir veri tipi yaratmaktır. Hazır bir veri tipi üzerinde hangi işlemlerin yapılabileceği derleyici tarfından bilinmektedir. Yaratılan yeni veri tipi (soyut veri tipi) üzerinde yapılabilecek işlemler de derleyiciye, sınıfa üye yordamlar (metodlar) tarafından öğretilir.
Gerçek Dünya:
Nesne = Nitelikler + Davranışlar
Yazılım Ortamı:
Nesne = Veriler + Yordamlar (Object = Data + Procedures)
Sınıf Bildiriminin Genel Biçimi:
class [sınıf_ismi] {
[private:]
...
...
[protected:]
...
...
[public:]
...
...};
Bir sınıf 3 bölümden oluşur:
1. Private
2. Protected
3. Public
Bir bölüm belirten anahtar sözcük ve iki nokta üst üste ile başlatılır, başka bir bölüm belirten sözcüğe kadar sürer. Birden fazla aynı bölüm belirten anahtar sözcük aynı sınıf bildirimi içerisinde kullanılabilir. Bölüm belirten anahtar sözcüklerin biri ya da hiçbirisi yazılmak zorunda değildir. Sınıf hiçbir bölüm belirten anahtar sözcükle başlatılmamışsa private bölüm anlaşılır. Okunabilirlik açısından sınıf isminin ilk harfi büyük geri kalan harfleri küçük yazılır. Bir yapı yalnızca veri elemanlarına sahiptir. Sınıflar hem veri hem fonksiyon içeren veri yapılarıdır. Yani normal yapılardan sınıfların fazlalıkları aynı zamanda fonksiyon da içermeleridir. Sınıf içerisinde bildirilen değişkenlere sınıfın veri elemanları(data member) sınıf içerisinde bildirilen fonksiyonlara ise sınıfın üye fonksiyonlar(member function) denir(daha yüksek seviyeli nesne yönelimli dilllerinde metod ismi de kullanılır). Veri elemanları ve üye fonksiyonları sınıfın herhangi bir yerinde yazılabilir.
Üye fonksiyonların sadece prototipleri sınıf içerisine konur. Tanımlamaları sınıf bildiriminden sonra yapılır. Ancak genellikle protected bölümü pek kullanılmaz, sınıfın veri elemanları private bölüme üye fonksiyonları public bölüme yazılır.
4.2 Bir Sınıf Türünden Nesnenin Tanımlanması
Genel biçimi:
[class] <sınıf_ismi> <nesne_ismi>;
class Sample x;
Sample y;
class anahtar sözcüğü yazılmayabilir. C++'ta yapı türünden nesne tanımlarken struct anahtar sözcüğü de kullanılmayabilir. Bir sınıf nesnesi için sınıfın toplam veri elemanları kadar yer ayrılır.
/*-----class1.cpp-----*/
#include <stdio.h>
class Sample {
private:
int a, b;
public:
void fonk(void);
};
void main(void)
{
Sample x;
printf("%d\n", sizeof(x));
}
/*-----------------------*/
4.3 Üye Fonksiyonları Tanımlanması
Üye fonksiyonları prototipleri sınıf bildirimi içerisine yerleştirilir, tanımlamaları dışarıda aşağıdaki gibi yapılır.
[geri dönüş değerinin türü] <sınıf isim> :: <fonksiyon ismi> ([parametreler])
void Sample::fonk(void)
{
}
İki tane iki nokta üstüste C++'a özgü bir operatördür. Üye fonksiyonlar amaç koda paramet re türleri ve sınıf isimleriyle kombine edilerek yazılırlar.Yani aynı isimli ve aynı parametre ya pısına sahip bir üye fonksiyonu ve global bir fonksiyon tanımlanabilir. Hiçbir sınıfa ait olma yan fonksiyonlara global fonksiyon denir.
4.4 Sınıfın Veri Elemanlarına ve Üye Fonksiyonlarına Erişim
Sınıfın veri elemanlarına ve üye fonksiyonlarına nokta operatörüyle erişilir. Bir üye fonk siyonu ancak aynı sınıf türünden bir nesneyle çağırılabilir. Eğer nesne olmadan çağırılırsa global bir fonksiyonun çağırıldığı anlaşılır.
X.fonk(); /*üye fonksiyonu çağırılmış*/
fonk(); /*global fonkiyon çağırılmış*/
/*-----class2.cpp-----*/
#include <stdio.h>
class Sample {
public:
int a, b;
public:
void fonk(void);
};
void Sample::fonk(void)
{
printf("I'm sample fonk..\n");
}
void fonk(void)
{
printf("I'm global fonk..\n");
}
void main(void)
{
class Sample X;
X.a = 10;
X.b = 20;
X.fonk();
fonk();
}
/*-----------------------*/
Bir üye fonksiyon içerisinde sınıfın hangi bölümünde tanımlanmış olursa olsun bütün veri elemanları ve üye fonksiyonlarına doğrudan erişilebilir. Yani sınıfın veri elemanları sınıfın üye fonksiyonları arasında ortak olarak kullanılmaktadır. Bir üye fonksiyon içerisinde kullanılan üye fonksiyonları o üye fonksiyon hangi sınıf nesnesiyle çağırılmışsa o sınıf nesnesinin elemanları olur.
/*-----class3.cpp-----*/
#include <stdio.h>
class Sample {
public:
int a;
public:
void fonk1(int x);
void fonk2(void);
};
void Sample::fonk1(int x)
{
printf("I'm sample fonk1..\n");
a = x;
}
void Sample::fonk2(void)
{
printf("%d\n", a);
}
void main(void)
{
class Sample X;
X.fonk1(50);
Sample Y;
Y.fonk1(100);
X.fonk2();
Y.fonk2();
}
/*-----------------------*/
Bir üye fonksiyonu içerisinde sınıfın bir diğer üye fonksiyonu da doğrudan çağırılabilir. Sınıfın a üye fonksiyonu X nesnesiyle çağırılmış olsun, a üye fonksiyonu içerisinde b üye fonksiyonu doğrudan çağırılabilir. Bu durumda b üye fonksiyonu içerisinde kullanılan veri elemanları X sınıf nesnesine ilişkindir.
/*-----class4.cpp-----*/
#include <stdio.h>
class Sample {
public:
int a;
public:
void fonk1(int x);
void fonk2(void);
};
void Sample::fonk1(int x)
{
printf("I'm sample fonk1..\n");
a = x;
fonk2();
}
void Sample::fonk2(void)
{
printf("%d\n", a);
}
void main(void)
{
class Sample X;
X.fonk1(50);
}
/*-----------------------*/
4.5 Bir Sınıfın Başka Bir Sınıfa Üye Olması
Nesneye dayalı programlamanın amaçlarından biri, daha önce yazılmış bir program kodunun yeniden kullanılmasını kolaylaştırmaktır. Önceden yazılmış bir program kodunu yeni yazılan programa adepte etmenin çeşitli zorlukları (kodun anlaşılır olmayışı, program düzeninin farklılığı, modüller arası bağımlılık, vs.) vardır. Nesneye dayalı programlamanın doğal yapısı bu mantıkla yazılmış olan eski kodun yeni programa uyarlanmasını kolaylaştırmaktadır. Bunun için iki yöntem kullanılmaktadır. Bunların ilki bir sınıfın başka bir sınıfa üye yapılmasıdır. İkinci olanak ise ileride ayrıntılı olarak incelenecek olan, daha esnek bir programlama olanağı sağlayan kalıtım özelliğidir.
C++’ta, daha önce tanımlanmış olan bir sınıf, başka bir sınıfın içinde üye eleman olarak yer alabilir.
class kesir{ // Kesir yapısı tanımlayan sınıf
int pay, payda;
public:
Kesir(int py=0, int pyd=1){
pay=py;
payda=pyd;
}
:
};
class ComplexKes{ // Karmaşık sayıları tanımlayan sınıf
kesir re, im; // kesir sınıfı tipinden iki veri
public:
ComplexKes(int re_in=0,int im_in=0):re(re_in),im(im_in)
{
: // Gerekl islemler
}
}
Ana programda aşağıdaki tanımlama yapıldığında önce ComplexKes kurucu fonksiyonu canlanacaktır. Ancak bu fonksiyona ait işlemler gerçekleştirilmeden önce üye sınıflara (kesir sınıfı) ait kurucu fonksiyonlar yürütülecektir. Ardından asıl kurucu fonksiyona ait işlemler gerçekleştirilecektir.
ComplexKes c(8,3);
Örnekte görüldüğü gibi, bir sınıfa üye nesne yaratırken kullanılan yazılım, normalde bir sınıftan bir nesne yaratırken kullanılandan biraz farklıdır. Normalde yaratılan nesneye başlangıç değeri atanacaksa, bu değer nesnenin adının yanına yazılarak kurucu fonksiyona aktarılır. Ancak üye nesneler için bu yöntem kullanılamaz. Üye nesnelerin sadece bildirimi yapılır. Başlangıç değerleri ancak, üyeyi kapsayan sınıfın kurucu fonksiyonu çağrıldıktan sonra verilebilir.
ComplexKes(int re_in=0,int im_in=0)
{ // Deişkenler 'private' olduğundan aşağıdaki erişimler yasaktır.
re.pay=re_in;
re.payda=1;
im.pay=im_in;
im.payda=1;
}