C++ Tümü

Başlatan XX_CİHAN_XX, 04 Temmuz 2006, 12:26:27

XX_CİHAN_XX

C++ NASIL BİR PROGRAMLAMA DİLİDİR?

   C++ nesne yönelimli programlama tekniğinin uygulanabilmesi için C'nin genişletilmiş bir biçimidir. Nesne yönelimli programlama(object oriented programming)  tekniği ve C++ B.Stroustroup tarafından geliştirilmiştir. Tasarım 70'li yılların ikinci yarısından başlanmış olsa da bütün dünyada yaygınlaşması ve kabul görmesi 80'li yılların sonlarına doğru mümküm olmuştur. Nesne yönelimli programlama tekniği(NYP) özellikle büyük kodların üstesinden gelebilmek amacıyla tasarlanmıştır. Tasarımı C++ üzerinde yapılmış olmasına karşın bugün pek çok yüksek seviyeli programlama dilleri bu tekniği desteklemektedir. C++ ve nesne yönelimli programlama tekniğinin en belirgin uygulama alanlarından birisi WINDOWS altında programlamadır. WINDOWS karmaşık ve yüksek yüksek seviyeli bir işletim sistemidir. WINDOWS altında program geliştirebilmek için uzun kodlar yazmak gerekir. Bu nedenle WINDOWS altında C ile değil C++ ile ve NYP tekniğini kullanarak program yazmak daha etkin bir çözümdür. NYP tekniğinin uygulanabilmesi için çalıştığımız sistemin kaynaklarının yeterince geniş olması gerekir. (Yani hızlı bir mikro işlemci, büyük RAM ve DISK ve iyi bir işletim sistemi)

         C++'IN C'DEN FARKLILIKLARI




      NYPT İLE DOĞRUDAN    SINIF YAPISI
      İLİŞKİSİ OLMAYAN
      FARLILIKLARI VE
      FAZLALIKLARI      

   İki düzeyde değerlendirilebilir.
1-)NYPT ile doğrudan ilişkisi olayan farkılılıklar ve fazlalıklar
2-)Sınıf yapısı

   Sınıf(class) C'deki yapı(struct)'lara benzer bir veri yapısıdır. NYPT sınıflar kullanılarak program yazılması tekniğidir. Kursun %80'i sınıf yapısının yapısı ve kullanılması üzerine ayrılmıştır.

C++'IN NYPT İLE DOĞRUDAN İLİŞKİSİ OLMAYAN FARLILIKLARI VE FAZLALIKLARI

   C++ derleyicileri C derleyicisini de içermek zorundadır. Yani C++ derleyicisi demek hem C hem de C++ derleyicisi demektir. Derleyici dosyanın uzantısına bakarak kodun C'de mi yoksa C++'ta mı yazılmış olduğuna karar verir. C'de ise uzantısı c, C++'ta yazılmışsa uzantısı cpp'dir.

1-) C++'ta yerel değişkenlerin bildirimleri blokların başında yapılmak zorunda değildir.

Standart C'de yerel değişkenler blokların başında bildirilmek zorundadır. Yani küme parantezi açıldıktan sonra daha hiçbir fonksiyon çağırılmadan ve işlem yapılmadan yapılmalıdır. Bu tasarımın nedeni programcının bildirimin yerini kolay bulabilmesini sağlamaya yöneliktir. Oysa C++'ta terel değişklenler bloğun herhangi bir yerinde bildirilebilir. Bir değişkenin kullanıma yakın bir bölgede bildirilmesi C++ tasarımcılarına göre daha okunabilirdir. (Değişken kavramı nesne isimlerini, struct, union ve enum isimlerini ve enum sabitlerini, typedef isimlerini içeren genel bir terimdir.) O halde C++'ta yerel değişkenin faaliyet alanı bildirim noktasından blok sonuna kadar olan bölgeyi kapsar. Ne olursa olsun bir blok içerisinde aynı isimli birden fazla değişken bildirimi yapılamaz.

C++'da for döngüsünün birinci kısmında bildirim yapılabilir. Örnek olarak:

for(int i = 0,j = 20; i + j < 50; ...){ }

Tabii while döngüsünün ve if deyiminin içerisinde bildirim yapılamaz.

#include <stdio.h>
#define SIZE        100

void main(void)
{
   for(int i = 0; i < SIZE; ++i)
       printf("%d\n", i);
}

   Böyle for döngüsünün içerisinde bildirilmiş değişkenlerin faaliyet alanları bildirildiği yerden for döngüsünün içinde bulunduğu bloğun sonuna kadar etkilidir. if, for, switch, while gibi deyimlerden sonra blok açılmamış olsa bile gizli bir bloğun açıldığı düşünülmelidir.

{
   for (int i = 0; i < 100; ++i) {
      for (int j = 0; j < 100; ++j) {
      }
      printf(%d\n", j);    /*geçerli*/
   }
   printf("%d\n" ,i);      /*geçerli*/
   printf("%d\n", j);      /*geçersiz*/
}

{
   for (int i = 0; i < 100; ++i)
      for (int j = 0; j < 100; ++j) {
      }
   j = 10;      /*geçersiz*/
   i = 10;      /*geçerli*/
}

2-)    C++'ta // ile satır sonuna kadar yorumlama yapılabilir.

   C++'ta /* */ yorumlama biçiminin yanı sıra kolaylık olsun diye // ile satır sonuna kadar yorumlama biçimi de eklenmiştir. Son senelerde böyle bir yorumlama biçimi standart C'de de kullanılmaya başlanmıştır. Ancak ANSI C standartlarında tanımlı değildir. Taşınabilirlik bakımından bu yorumlama biçimini standart C'de kullanmak tavsiye edilmez.

3-)C++'ta çağırılan fonksiyon eğer çağıran fonksiyonun yukarısında tanımlanmamışsa fonksiyon prototipi zorunludur.

C ‘de bir fonksiyonun çağırıldığını gören derleyici fonksiyonun çağırılma noktasına kadar fonksiyonun tanımlamasıyla ya da prototipi ile karşılaşmamışsa geri dönüş değerini int olarak varsayar ve kod üretir. Dolayısıyla aşağıdaki örnek C'de geçerlidir.



void main(void)
{
   int x;
   
   x = fonk();
}

int fonk()   /*Bu durum C'de sorun olmaz ama C++'ta error verir.*/
{

}

Oysa C++'ta derleyicinin çağırılma noktasına kadar fonksiyonun tanımlamasıyla ya da  prototipiyle karşılaşması gerekir. Dolayısıyla yukarıdaki kod C++'ta error'dür. (NOT: CV++ ve nesne yönelimli programlama tekniği bug oluşturabilecek kodlardan kaçınılması temeline dayandırılmıştır. Yani garanti yöntemler kullanılmalıdır. Bu sebeple C'deki pek çok uyarı C++'ta error'e dönüştürülmüştür.)

4-)  C++'ta farklı parametre yapılarına sahip aynı isimli birden fazla fonksiyon tanımlanabilir.
   
void fonk(void)
{
}

void fonk(int x)
{
}

C'de ne olursa olsun aynı isimli birden fazla fonksiyon tanımlanamaz. Oysa C++'ta parametre yapısı sayıca ve/veya türce farklı olan aynı isimli birden fazla fonksiyon tanımlanabilir. Aynı isimli birden fazla fonksiyon varsa ve o fonksiyon çağırılmışsa gerçekte hangi fonksiyon çağırılmış olduğu çağırılma ifadesindeki parametre yapısı incelenerek belirlenir. Yani çağırılma ifadesindeki parametre sayısı ve türü hangisine uygunsa o çağırılmış olur. Geri dönüş değerinin farklı olması aynı isimli fonksiyon yazmak için yeterli değildir. Yani geri dönüş değerleri farklı fakat parametre yapısı aynı olan birden fazla fonksiyon tanımlanamaz.

#include <stdio.h>

void fonk(int x)
{
   printf("int = %d\n", x);
}

void fonk(long x)
{
   printf("long = %ld\n", x);
}

void fonk(void)
{
   printf("void\n");
}

void fonk(char *str)
{
   puts(str);
}

void main(void)
{
   fonk();      /*parametresi void olan fonksiyonu çağırır*/
   fonk(10);      /*parametresi int olan fonksiyonu çağırır*/
   fonk(100L);      /*parametresi long olan fonksiyonu çağırır*/
   fonk("merhaba");   /*parametresi karakter türünden gösterici olan fonksiyonu çağırır*/
}

İKİ ANLAMLILIK HATASI

C++'ta pek çok durumda derleyicinin birden çok seçenek arasında karar verememesinden dolayı error durumuyla karşılaşılır. Bu tür hatalara iki anlamlılık hataları denir. Yukarıdaki örnekte fonk(3.2); gibi bir çağırma yapılırsa "Ambiguity between 'fonk(int)' and 'fonk(long)'" hatasını verir. Aynı isimli birden fazla fonksiyon arasında seçme işlemi ancak parametre sayıları çağılma ifadesine uygun birden fazla fonksiyon varsa gerçekleşir. Parametre sayısı çağırılma ifadesine uygun tek bir fonksiyon varsa bu durumda tür uyuşmasına bakılmaz. C'de olduğu gibi otomatik tür dönüştürmesi yapılarak  o fonksiyon çağırılır.

C++ derleyicisi aynı sayıda parametrelere sahip birden fazla aynı isimli fonksiyonun bulunması durumunda çağırılma ifadesine tür bakımından uygun bir fonksiyon bulamazsa bu durum iki anlamlılık hatasına yol açar. Bu durumun 3 istisnası vardır:

1.   Fonksiyon char ya da short parametreyle çağırılmışsa char ya da short int parametreye sahip bir fonksiyon yok ancak int parametreye sahip bir fonksiyon varsa int parametreye sahip olan fonksiyon çağırılır.
2.   Fonksiyon float parametreyle çağırılmışsa ancak float parametreye sahip bir fonksiyon yok double parametreye sahip bir fonksiyon tanımlanmışsa bu durumda double parametreye sahip  olan fonksiyon çağırılır.
3.   Fonksiyon aynı türden const olmayan bir ifadeyle çağırılmışsa ancak aynı türden const parametreye sahip bir fonksiyon y,tanımlanmışsa tür uyuşumunun sağlandığı kabul edilir ve const parametreye sahip olan fonksiyon çağırılır.

C'de ve C++'ta tanımlanan ve çağırılan bir fonksiyon ismi .obj modül içerisine yazılmak zorundadır. .obj modül standardına göre aynı isimli birden çok fonksiyon modül içerisine yazılamaz. Standart C derleyicileri fonksiyon isimlerinin başına bir _ ekleyerek obj modülün içerisine yazarlar. Oysa C++ derleyicileri fonksiyon isimlerini parametre türleriyle kombine ederek obj modül içerisine yazarlar. Bu durumda C++'ta aynı isimli farklı parametrelere sahip fonksiyonlar sanki farklı isimlere sahiplermiş gibi obj modüle yazılırlar.

5-)  extern "C" ve extern "C++" bildirimleri

C++'ta normal olarak bütün standart C fonksiyonları çağırılabilir. Standart C fonksiyonları lib dosyalarının içerisine başında "_" bulunarak yani standart C kurallarıyla yazılmışlardır. Oysa bu fonksiyonların C++'tan çağırılmasıyla bir uyumsuzluk ortaya çıkar. Çünkü C++ derleyicisi çağırılan fonksiyonu obj modül içerisine başına "_" koyarak değil parametre türleriyle kombine ederek yani C++ kurallarıyla yazar. extern "C" bildirimi bir fonksiyonun prototipinin önüne ya da bir fonksiyonun tanımlamasının önüne getirilirse   /*örneğin:

extern "C" double sqrt(double);

veya

extern "C" void fonk(void)
{
.........
}
*/
derleyici bu fonksiyonu obj modül içerisine C kurallarıyla yani başına "_" koyarak yazar. Böylece C'de yazılmış olan C++'tan kullanılması mümkün olur. Bir grup fonksiyon yazım kolaylığı sağlamak için extern "C" bloğu içine alınabilir.

extern "C" {
void fonk(void);
void sample(void);
....
}

Bloğun içerisinde başka bildirimler ve kodlar bulunabilir. Ancak derleyici yalnızca bu bloğun içerisindeki fonksiyonlarla ilgilenir. Bu durumda standart C başlık dosyalarının içerisinde fonksiyonların extern "C" bildirimiyle prototipleri yazılmış olması gerekir. Aynı dosya hem C hem C++'ta include edilip kullanılabildiğine göre ve extern "C" bildirimi sadece C++ için geçerliyse bir problem ortaya çıkmaz mı? Bu problem önceden tanımlanmış cplusplus sembolik sabitiyle çözümlenmiştir:

#ifdef cplusplus
extern "C" {
#endif
.....
.....
.....
.....
.....
.....
#ifdef cplusplus
}
#endif

Bir de extern "C++" bildirimi vardır. Bu bildirim fonksiyon isimlerinin C++ kurallarına göre obj modülün içerisine yazılacağını anlatır. Zaten fonksiyonlar default olarak bu kurala göre yazılırlar. Bu bildirim ileriye doğru uyumu sağlamak için düşünülmüştür. Şu anda bir kullanım gerekçesi yoktur.

6-)   C++'ta dinamik bellek yönetimi new ve delete isimli iki operatörle yapılır.

Mademki C++ içerisinde bütün standart C fonksiyonları kullanılabiliyor, o halde dinamik bellek yönetimi malloc, claloc, realloc ve free fonksiyonlarıyla yapılabilir. Ancak bu fonksiyonlar nesne yönelimli programlama tekniğini uygulayabilmek için tasarlanmamıştır. Bu yüzden C++'ta yeni bir teknik kullanılmaktadır. C++'ta dinamik olarak tahsis edilme potansiyelindeki boş bölgelere free store denilmektedir(standart C'de heap denir).

new Operatörü

Genel biçimi:

new <tür> [<[uzunluk]>]

new int
new char
new double [10]
new float[n]
new char[strlen(s) + 1]

Eğer köşeli parantez olmadan sadece tür ismi ile tahsisat yapılırsa o türden bir elemanlık yer tahsis edilmiş olur. Örneğin:

new int 1 int'lik yer tahsis edilmiştir.

Eğer köşeli parantez içerisine ifade yazılarak kullanılırsa bu durumda o ifade ile belirtilen sayıda elemanlık alan tahsis edilir. new operatörü türü belirli bir alan tahsis eder. Yani new operatörüyle elde edilen adresin tür bileşeni çağırılma ifadesindeki tür ile aynı olur.

int *p;
p = new int;   /*   Burada sizeof(int) kadar byte tahsis ediliyor ve tahsis edilen   */    /*   alanın başlangıç adresi elde ediliyor. Bu adres int türündedndir.   */

char *p;
p = new int [10];   /*   C++'ta hatadır.   */
p = (char *)new int[10];   /*   Hata değil.   */

/*----------new1.cpp---------*/
#include <stdio.h>
#include <string.h>

void main(void)
{
   char *p;

   p = new char[30];
   gets(p);
   puts(p);
}
/*------------------------------*/

New bir operatördür. Ancak derleyici bu operatör kullanıldığında dinamik tahsisat işleminin yapılmasını sağlamak için dinamik tahsisat yapan bir fonksiyonun çağırma  kodunu amaç koda  ekler. Yani new bir operatör olmasına karşın tahsisat işlemi yerleştirilen bu fonksiyon sayesinde programın çalışma zamanı sırasında yapılmaktadır. Bu operatör öncelik tablosunun ikinci düzeyinde bulunmaktadır. Örneğin:
new int + n
gibi bir işlem geçerlidir. İşlemler:
İşlem 1 : new int
İşlem 2 : İşlem 1 + n

New operatörü tahsisat işlemini yapamazsa 0 değerini(NULL gösterici) üretir.

/*-------freestor.cpp------*/
/*free store alanının hesaplanması*/
#include <stdio.h>

#define BLOCKSIZE       1024

void main(void)
{
   long size = 0;
   char *p;

   for(;;){
       p = new char[BLOCKSIZE];
       if(p == NULL)
           break;
       size += BLOCKSIZE;
   }
   printf("Free store size = %ld\n", size);
}
/*---------------------------*/

Köşeli parantez içerisine yazılan ifade sabit ifadesi olmak zorunda değildir.

/*-----------new2.cpp---------*/
/*Tam olarak ad sosay uzunluğu kadar bellek tahsis eden fonksiyonun kullanılışı*/
#include <stdio.h>
#include <stdlib.h>

char *getname(void)
{
   char *p;
   char buf[80];

   printf("Adı Soyadı:);
   gets(buf);
   p = new char[strlen(buf) + 1)];
   if(p == NULL){
       printf("Cannot allocate memory..\n");
       exit(1);
   }
   strcpy(p, buf);
   return p;
}

void main(void)
{
char *p;

   p = getname();
   puts(p);
}
/*--------------------------------*/

delete OPERATÖRÜ

Delete operatöürü new operatörüyle tahsis edilmiş olan blokları serbest bırakmak için kullanılır. Genel biçimi:

1.   delete p;
2.   delete [] p;

Eğer tahsisat tek parça olarak yapılmışsa yani köşeli parantez kullanılmadan yapılmışsa silme işlemi köşeli parantez kullanılmadan yapılmalıdır. Örneğin:

int *p;
p = new int;
delete p;

Eğer tahsisat işlemi birden fazla eleman için yapılmışsa yani köşeli parantez kullanılarak yapılmışsa serbest bırakma işleminde de köşeli parantez kullanılmalıdır. Örneğin:

int *p;
p = new int[n];
delete [] p;

Burada köşeli parantez içerisine bir şey yazılmaz. delete operatörü unary prefix bir operatördür ve öncelik tablosunun ikinci düzeyinde bulunur.

delete p + 1; /*Hatalı*/
delete (p + 1);/*Doğru*/

Delete operatörünün operandı daha önce tahsis edilmiş olan bloğun başlangıç adresi olmalıdır.  Değilse beklenmeyen sonuçlar ortaya çıkabilir. Tabii derleici delete operatörüne karşılık amaç koda (object module'e) free gibi tahsis edilmiş bloğu serbest bırakan bir fonksiyon kodu yerleştirmektedir. new delete operatörlerinin tahsisat işlemlerinde kullandığı fonksiyon maloc, calloc, free fonksiyonları olmak zorunda değildir. Bu iki grup fonksiyon farklı tahsisat tabloları kullanıyor olabilir. Bu nedenle new delete operatörleriyle malloc, calloc, free gibi standart C fonksiyonlarını özel bir durum yoksa birlikte kullanmamak gerekir. Çünkü bir grup tarafından tahsis edilen alan diğer grup tarafından tahsis edilmemiş gibi gözükebilir.

Görüldüğü gibi C++'ta realloc fonksiyonun karşılığı bir operatör yoktur. Ancak böyle bir fonksiyon yazılabilir.

/*------------realloc.cpp---------------*/
void *Realloc(void *ptr, size_t newsize, size_t oldsize) /*size_t unsigned int*/
{
   void temp;

   temp = new char [newsize];
   memcpy(temp, ptr, oldsize);
   delete [] ptr;
   return temp;
}
/*----------------------------------------*/


Kullanımı:
p = new char [10];      /*   10 * sizeof(char) kadar bellek tahsis edildi      */
p = Realloc(p, 20, 10);   /*   Tahsis edilmiş alan 20 * sizeof(char)'e büyütüldü   */


set_new_handler FONKSİYONU

Normal olarak new oparetörü başarısızlıkla sonuçlandığında 0 adresine geri döner ve bu adresin test edilmesi gerekir. Ancak her new kullanımında bu adresin test edilmesi yerine daha etkin bir yöntem kullanılmaktadır. new operatörü başarısız olduğunda set_new_handler fonksiyonu ile belirlenen fonksiyonu çağırmaktadır. Böylece her defasında kontrol yapılmasına gerek kalmaz.

set_new_handler(void (*ptr)(void));

set_new_handler'a parametre olarak geri dönüş değeri void parametresi void olan bir fonksiyonun adresi verilir. Artık başarısızlık durumunda bu fonksiyon çağırılacaktır. new operatörü başarısızlık durumunda belirlenen fonksiyonu çağırır ve bu fonksiyon çağırıldıktan sonra tekrar tahsisat işlemini yapar. Yine başarısız olursa tekrar fonksiyonu çağırır ve bu böyle devam eder. Yani aşağıdaki algoritmadaki gib çalışır:
for(;;){
   if(boşyer var mı)
      return boşyer;
else
   set_new_handler();
}

/*-----------snhandle.cpp---------------*/
#include <stdio.h>
#include <new.h>
#include <stdlib.h>

long size = 0;

void myhandler(void)
{
   printf("Free store size=%ld\n", size);
   exit(1);
}
void main(void)
{
   void *ptr;
   void *oldhandler;

   oldhandler = set_new_handler(myhandler);
   for(;;){
       ptr = new char [1024];
       size += 1024;
   }
}
sen_new_handler(oldhandle);   /*handler eski haline dönüştürüldü*/
/*------------------------------------------*/

set_new_handler'ın prototipi new.h içindedir.


7-) Bir adresin farklı türden bir göstericiye atanması ve adres olmayan bir bilginin bir göstericiye atanması durumu uyarı değil error olarak değerlendirilir.

Adres işlemlerinde tür uyuşmazlıkları C++'ta eror olarak değerlendirilir. Oysa standart C derleyicileri böyle durumlarda en fazla uyarı verirler. Ancak void göstericiye herhangi bir türden adres atanabilir. Fakat void bi adresin herhangi bir göstericiye atanması error olarak değerlendirlir(bu durum C'de en fazla uyarı olaak değerlendilir). Tabii tür dönüştürme operatörüyle her tür her türe atanabilir.

/*----------fark7.cpp----------*/
void main(void)
{
   int s[100];
   char *t;

   t = s;      /*    "Cannot convert 'int *' to 'char *'" hatasını verir   */
   t = (char *)s;   /*   Hata vermez                  */
}
/*--------------------------------*/

Benzer biçimde const bir değişkenin adresi ancak const bir göstericiye atanmalıdır.

const int x;
int *y;
conts int *p;
y= &x;      /*   Hata verir      */
p = &x;   /*   Hata vermez      */

8-)  const bildirimi ile yaratılmış bir değişken sabit ifadesi gibi işlem görür.

C++'ta const bir değişken için yine bellekte yer ayrılır. Ancak const değişken kullanıldığında derleyici eğer const değişkene ilk değer sabit ifadesiyle verildiyse  derleyici doğrudan o sabit ifadesini kullanır. Tabii const değişkene verilen ilk değer sabit ifadesi değilse bu consta değişken kullanıldığında derleyici doğrudan bir sayı yerleştiremez, const değişkenin kendisini yerleştirir.

const int MAX = a + 100;
const int MIN = 1;
y = MAX;      /*   Burada bir sayı yazamaz      */
y = MIN;      /*   Burada MIN yerine 1 yazılabilir   */
const int SIZE = 10;
int a[SIZE];      /*   C++'ta geçerli C'de geçerli değil   */

Const değişken için yine de bellkte yer ayrılır. Bu durumda const değişkenin adresi alınabilir. Bu yolla const deişkenin içeriği de değiştirilebilir. Tabii bu değiştirme programın çalışma zamanı içerisinde olduğundan sonucu değiştirmez.

/*----------fark8.cpp------------*/
#include <stdio.h>

void main(void)
{
   const int SIZE = 10;
   int *p;

   p = (int *)&SIZE;
   *p = 20;
   printf("%d\n", SIZE);
}
/*--------------------------------*/

9-) C++'ta statik ömürlü değişkenlere sabit ifadesiyle ilk değer verme zorunluluğu yoktur.

Global değişkenler ve statik yerel değişkenler gibi statik ömürlü değişkenlere ilk değer C'de sabit ifadesiyle verilmek zorundadır. Çünkü statik ömürlü değişkenler amaç kod içerisine ilk değerleriyle yazılırlar. Exe dosyasının içerisinde yer alırlar. Bunun mümkün olabilmesi için verilen ilk değerlerin derleme aşamasında belirlenmiş olması gerekir. Derleme aşamasında tespit edilmesi için ifadenin sabit ifadesi olması gerekir. Oysa C++'ta statik ömürlü değişkenlere her türden sıradan bir ifadeyle ilk değer verilebilir. Bu değişkenler 0 ilk değeriyle amaç koda yazılırlar. Programın çalışma zamanı sırasında ve main fonksiyonundan önce ilk değerini alırlar.

10-) Parametre değişkenlerinin default değerler alması(default function arguments)

C++'ta fonksiyon çağırılırken bir parametre belirtilmemişse ona ilişkin parametre değişkeni default bir değer alabilir. Böyle bir durum C'de yoktur. Bir parametre değişkeninin default değer alması durumu fonksiyon tanımlanırken ya da prototip bildiriminde paramere değişkeninden sonra eşittir operatörüyle belirtilmelidir.

/*---------fark10.cpp----------*/
#include <stdio.h>

void fonk(int x = 10, int y = 20)
{
   printf("x = %d y = %d\n", x ,y);
}

void main(void)
{
   fonk(100, 200);   /*    x = 100   y = 200   */
   fonk(100);      /*   x = 100   y = 20      */
   fonk();      /*   x = 10      y = 20      */
}
/*--------------------------------*/

Bir parametre değişkeni default değer almışsa onun sağında bulunanların hepsi default değerler almak zorundadır.

void fonk(int x = 10, int y)   /*   Hata verir   */
{
}

void fonk(int x, int y = 20)   /*   Hata vermez   */
{
}

Default değer almamış olan bütün parametre değişkenleri için çağırılma ifadesinde parametre yazılmak zorundadır. Default değer alan parametre değişkenlerine sahip fonksiyonlarla aynı isimli başka fonksiyonların birlikte bulunması durumunda iki anlamlılık hataları oluşabilir. İki anlamlılık hataları fonksiyonların tanımlanması sonucunda değil çağırılması sonucunda ortaya çıkmaktadır.

/*   İki anlamlılık hatası örneği   */
#include <stdio.h>

void fonk(int x, int y = 20)
{
   printf("%d  %d\n", x, y);
}

void fonk(int x)
{
   printf("%d\n", x);
}

void main(void)
{
   fonk(100, 200);   /*   Hata vermez         */
   fonk(100);      /*   İki anlamlılık hatası verir   */
}
/*------------------------------------------*/

Bir gösterici parametresi de default değer alabilir.

/*   Göstericiye default değer   */
#include <stdio.h>

void message(const char *p = "Success")
{
   puts(p);
}

void main(void)
{
   char *p = "Ali";

   message(p);
   message();
}
/*-------------------------------------------*/

Default Parametre Değişkenlerine Sahip Fonksiyonların Kullanılma Nedenleri

Çok sayıda parametrelere sahip fonksiyonlar söz konusu ise ve bu parametre değişkenlerinin belli bölümüne çağırma sırasında aynı değerler atanıyorsa default parametre değişkenlerinin kullanılması büyük bir yazım kolaylığı sağlar. Fazla sayıda parametrenin yazılmaması hem programcının iş yükünü azaltır, hem de okunabilirliği arttırır.

#include <stdio.h>
#include <stdlib.h>
void *myitoa(int n, char *str, int base = 10)
{
   return itoa(n, str, base);
}

void main(void)
{
   char s[100];

   myitoa(123, s);
   puts(s);
}

Default değer alan parametre değişkeni kullanılırken dikkat etmek gerekir. Bir fonksiyon % 90 aynı parametre değerleriyle çağırılıyorsa default parametre değişkeni kullanılmalıdır. "Hiçbir değer almayacağına bari şu değeri alsın" fikriyle kullanılmamalıdır. Böylesi kullanımlar kodu inceleyen kişiyi yanıltırlar. Bazen parametre değişkenine verilen default değerin özel bir anlamı olmaz. Bu default değer fonksiyonun default parametreyle çağırılıp çağırılmadını tespit etmek amacıyla kullanılır. Gerçek default değerler fonksiyonun içerisinde ve bir dizi işlemlerle elde edilir. Örneğin

#define DEFAULT_CALL        (-1)

void writefile(void *ptr, unsigned size, long offset = DEFAULT_CALL)
{
   if(offset != DEFAULT_CALL)
       fseek(fp, offset, SEEK_SET);
   fwrite(ptr, 1, size, fp);
}

void main(void)
{
   double x = 10.2;

   writefile(&x, sizeof(double));
}

Default Değer Alan Parametre Değişkenlerine Sahip Fonksiyonların Prototipleri

Böyle fonksiyonların prototiplerinde dafault parametre değerleri belirtilmelidir. Prototip yazma işlemi değişken isimlerini kullanarak ya da kullanmayarak yapılabilir. Örneğin aşağıdaki iki prototip de geçerlidir.

void sample(int = 10, int = 20);
void sample(int a = 10, int b = 20);

Prototipi yazılan fonksiyon aynı modül içerisinde tanımlanıyorsa(yani kütüphane içerisinde değilse) tanımlama sırasında bir daha bu default değerler yazılamaz. Yani default değerler ya prototipte ya da tanımlama sırasında belirtilmek zorundadır. Her ikisinde birden belirtilemezler. Tavsiye ediln kullanım prototipte belirtilmesi, tanımlama da belirtilmemesidir.

void sample(int x = 10, int y = 20);

void sample(int x  =10, int y = 20)   /*   Hata verir   */
{
}

void sample(int x, int y)      /*   Hata vermez   */
{
}



11-)C++'ta göstericilere benzeyen ve ismine referans denilen ayrı bir tür vardır.

Referans Türünden Bir Göstericinin Tanımlanması

Genel biçimi:

<tür> &<referans_ismi> = <nesne>

Örnek:

int a = 10;
int &b = a;

double x;
..........
double &y = x;

Bir referans ilk değer verilerek tanımlanmak zorundadır. Örneğin:

int &r;         /*   hata   */
double &r = 10.2;   /*   hata   */

Referansa verilen ilk değer aynı türden bir nesne olmak zorundadır.

double x = 10 ;
int &r = x;   /*   Hata. Farklı türden bir nesneyle ilk değer verilmiş.   */
int &r = a;   /*   Okunuşu: r int türünden bir referanstır   */

Referanslar bir çeşit düzeyi yüksek göstericidir. Referansların içerisinde adres bilgisi bulunur. Derleyici bir referans tanımlandığında ilk değer olarak verilen nesnenin adresini referansın içerisine yerleştirir. Referansları iyi anlayabilmek için onların eşdeğer gösterici karşılıklarını düşünmek gerekir. Eş değer gösterici karşılığı referans yerine gösterici kullanıldığında elde edilecek eş değer kod anlamına gelir.

int a = 10;
int &b = a;

Eşdeğer karşılığı:

int a = 10;
int *b = &a;

Bir referans ilk değer verildikten sonra kullanıldığında artık referans içerisindeki adres değil referans içerisindeki adreste bulunan bilgi temsil edilir.






/*----------fark11.cpp--------------*/

#include <stdio.h>

#if 1

void main(void)      /*   referans kullanımı   */
{
   int a = 10;
   int &b = a;

   b = 50;
   printf("%d  %d\n", b, a);
}

#endif

#if 0

void main(void)      /*   referansın gösterici karşılığı   */
{
   int a = 10;
   int *b = &a;

   *b = 50;
   printf("%d  %d\n", *b, a);
}

#endif
/*-------------------------------------*/

int a = 10;
int &b = &a;      /*   Hata: &a int türünden değil adres türündendir   */

Referansların Fonksiyon Parametresi Olarak Kullanılması

Referanslar fonksiyon parametresi olarak kullanılabilirler. Madem ki bir referans aynı türden bir nesneyle ilk değer verilerek tanımlanmak zorundadır, o halde parametresi referans olan fonksiyonlar aynı türden bir nesnenin kendisiyle çağırılmak zorundadır.

/*   fonksiyon parametresi olan referans örneği   */

#include <stdio.h>

#if 1   /*   parametresi referans   */
void fonk(int &a)
{
   a = 20;
}

void main(void)
{
   int x = 10;

   fonk(x);
   printf("%d\n", x);
}
#endif

#if 0   /*   gösterici karşılığı   */
void fonk(int *a)
{
   *a = 20;
}

void main(void)
{
   int x = 10;

   fonk(&x);
   printf("%d\n", x);
}
#endif
/*------------------------------------------------------------*/

Bir C programında fonk(a) gibi bir çağırma işlemiyle a değiştirilemez. Oysa C++'ta böyle bir çağırma fonksiyonun parametre değişkeni bir referans ise a paametresini değiştirebilir. Klasik bir C bakış açısıyla parametre olan a'nın değiştirilmeyeceği sanılabilir. Okunabilirliği kuvvetlendirmek için eğer parametreyi değiştirecek bir fonksiyon tasarlanacaksa bunun için referans değil gösterici kullanılmalıdır. Fonksiyonun parametre değişkeni referans ise derleyici tarafından otomatik olarak yapılan bir adres aktarımı söz konusudur.

Referans uygulaması   Gösterici eşdeğeri
int a = 10;
int &r1 = a;
int &r2 = r1;

r2 = 20;
printf("%d\n", r1);   int a = 10;
int *r1 = &a;
int r2 = &r1;

*r2 = 20;
printf("%d\n", *r1);

/*-----referans.cpp-----*/
#include <stdio.h>

#if 1   /*  referans örneği     */

void main(void)
{
   int a = 10;
   int &a1 = a;
   int &a2 = a1;

   a2 = 20;
   printf("%d\n", a1);
}

#endif

#if 0   /*gösterici eşdeğeri    */

void main(void)
{
   int a = 10;
   int *a1 = &a;
   int *a2 = a1;

   *a2 = 20;
   printf("%d\n", *a1);
}

#endif
/*-------------------------*/

/*-----referan1.cpp-----*/
#include <stdio.h>

void main(void)
{
   int a = 10;
   int &b = a;


   printf("%p  %p\n", &a, &b);
}
/*--------------------------*/

Bir referans & operatörüyle adres alma işlemine sokulabilir. Bu durumda elde edilen değer referans içerisinde bulunan adreste bulunan nesnenin adresidir. Bu da referans içerisindeki adresle aynı olmak zorundadır. Bir referansın da bir adresi vardır. Ama o adres değeri geçerli bir ifdade ile elde edilemez. r bir referans olmak üzere & &r; ifadesi geçerli değildir. Çünkü bu ifadenin eşdeğer gösterici karşılığı & &*p;'dir ve &*p bir nesne değildir.


Yapı Değişkenlerinin Referans Yoluyla Fonksiyonlara Geçirilmesi

Bir yapı değişkeninin fonksiyona aktarılmasında doğru teknik yapı değişkeninin adresinin fonksiyona geçirilmesidir. Yani fonksiyon yapı değişkeninin adresiyle çağırılır, fonksiyonun parametre değişkeni o yapı türünden bir gösterici olur. Fonksiyonun içerisinde elemana ok(->) operatörüyle erişilir. Ancak C++'ta aynı etkinlikte olmak üzere referansla aktarım da söz konusudur. Yani fonksiyon yapı değişkeninin kendisiyle çağırılır. Fonksiyonun parametre değişkeni o yapı türünden bir referans olur. Fonksiyon içeriisnde elemana nokta operatörüyle erişilir.

/*----------referan2.cpp-------------*/
#include <stdio.h>

struct PERSON{
   char *name;
   int no;
};

void disp(struct PERSON &r)
{
   printf("%s  %d\n", r.name, r.no);
}

void main(void)
{
   struct PERSON per = {"Ali Serçe", 123};

   disp(per);
}
/*--------------------------------------*/

Yapıların referans ya da gösterici yoluyla fonksiyonlara aktarılması tamamen eşdeğer kullanımlardır.

const Referanslar

Bir referans da const olarak tanımlanabilir.

Referans örneği   Gösterici eşdeğeri
int a = 10;
const int &b = a;

b = 20;  /* Hata */   int a = 10;
const int *p = &a;

*p = 20;  /* Hata */

Const bir referans, gösterdiği yer const olan const bir göstericiye eşdeğerdir. Yani böyle referanslar sol tarafa değeri olarak kullanılamaz. Çünkü referans içerisinde bulunan adresteki bilgi const yapılmıştır. Const referanslar da okunabilirliği arttırmak amacıyla fonksiyon parametresi olarak kullanılırlar.

void disp(const struct PERSON &r);
Fonksiyonun referans olan parametresi de default argüman alabilir.

int x;

void fonk(int &a = x)   /*fonksiyonun referans olan parametresi default değer almış*/
{
...
}

char &a = "Ali";   /*   Doğru bir kullanımdır      */

Fonksiyonun Geri Dönüş Değerinin Referans Olma Durumu

Return ifadesiyle geri dönüş değerinin oluşturulması aslında derleyici tarafından tahsis edilen geçici bir bölgeye yapılan atama işlemidir. Yani return ifadesi önce geçici bir bölgeye yerleştirilir, sonra oradan alınarak kullanılır. Fonksiyonun geri dönüş değerinin türü bu geçici bölgenin türüdür. Bir fonksiyonun geri dönüş değeri referans olabilir. Bu durumda fonksiyonun geri dönüş değerine ilişkin geçici bölge referans türündendir. Bir referansa bir nesneyle ilk değer verileceğine göre böyle fonksiyonları return ifadelerinin de nesne olması gerekir.

Gösterici eşdeğeri   Referans örneği
/*-----referan3.cpp-----*/
#include <stdio.h>

int a = 10;

int *fonk(void)
{
   return &a;
}

void main(void)
{
   *fonk() = 20;
   printf("%d\n", a);
}   /*------referan4.cpp-----*/
#include <stdio.h>

int a = 10;

int &fonk(void)
{
   return a;
}

void main(void)
{
   fonk() = 20;
   printf("%d\n", a);
}


Artık bu fonksiyon kullanıldığında referans kullanılıyor gibi işlem göreceğinden return ifadesindeki nesne anlaşılır. Böyle fonksiyonların geri dönüş değeri nesne belirtir ve sol taraf değeri olarak kullanılabilir. Özetle referansa geri dönen bir fonksiyonun geri dönüş değeri kullanıldığında return ifadesindeki nesnenin kullanıldığı anlaşılır.

Bir Referansa Farklı Bir Türden Bir Nesneyle İlk Değer Verilmesi Durumu

Böyle bir durumda önce referansla aynı türden geçici bir değişken yaratılır. Verilen ilk değeri bu geçici değişkene atar, tabii otomatik tür dönüştürülmesi olur ve yaratılan bu geçici bölgenin adresi referansa aktarılır.

/*-----referan5.cpp-----*/
#include <stdio.h>

void main(void)
{         /*   Eşdeğeri      */
   double x = 3.2;   /*   double x =3.2;      */
   int &r = x;      /*   int temp = x;      */
         /*   int &r = temp;      */
   r = 5;
   printf("%f\n", x);
}
/*--------------------------*/

Tabii böylesi bir durumda derleyiciler bir uyarıyla durumu bildirirler.


Bir Referansa Sabitle İlk Değer Verilmesi Durumu

Bir referansa bir sağ taraf değeriyle de ilk değer verilebilir. Bu durumda ilk değer olarak verilen sağ taraf değeri derleyici tarafından oluşturulan geçici bir bölgenin içerisine aktarılır. Geçici bölgenin adresi de referansa yerleştirilir.


Referans örneği   Eşdeğeri
/*-----referan6.cpp-----*/
#include <stdio.h>

void main(void)
{
   int &r = 10;

   r = 50;
   printf("%d\n", r);
}   int temp;
int &r = temp;

   Böyle iki problemli ilk değer verme durumlarından da kaçınmak gerekir. Her iki durumda da derleyici uyarı mesajı verecektir.

Göstericilerle Referanslar Arasındaki Benzerlikler ve Farklılıklar


-   Göstericiler de referanslar da adres tutan nesnelerdir.
-   Referansın içerisindeki adres bir daha değiştirilemez ama göstericinin içerisindeki adres değiştirilebilir.
-   Diziler türü ne olursa olsun, referans yoluyla referanslara geçirilemezler. Çünkü dizi elemanlarına erişmek için adres arttırımı yapmak gerekir.
-   Referanslar tek bir elemanı fonksiyona geçirmek için kullanılabilirler.

12-)C'de enum türü ile int türü tamamen aynıdır.
Yani enum türünden bir değişkene int türünden bir değer atanabilir. Oysa C++'ta enum türü ayrı bir türdür ve enum türünden değişkenlere ancak enum türünden sabitler atanabilir.


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şkeni anlatmakta kullanılır. Nesne yönelimli programlama tekniği sınıflar kullanılarak program yazma tekniğidir).

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 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.

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));
}
/*-----------------------*/

Ü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 parametre türleri ve sınıf isimleriyle kombine edilerek yazılırlar. Yani aynı isimli ve aynı parametre yapısına sahip bir üye fonksiyonu ve global bir fonksiyon tanımlanabilir. Hiçbir sınıfa ait olmayan fonksiyonlara global fonksiyon denir.

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 fonksiyonu 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);
}
/*-----------------------*/

Sınıf Faaliyet Alanı(class scope)


C'de dardan genişe doğru 3 tür faaliyet alanı vardır:
1.   Blok faaliyet alanı
2.   Fonksiyon faaliyet alanı
3.   Dosya faaliyet alanı

C'de ve C++'ta aynı faaliyet alanına ilişkin birden fazla değişken aynı isimle tanımlanamaz. Ancak farklı faaliyet alanına ilişkin aynı isimli birden fazla değişken tanımlanabilir. Bir blok içerisinde birden fazla aynı isimli değişken faaliyet gösteriyorsa o blok içerisinde dar faaliyet alanına sahip olan erişilebilir.

C++'ta sınıf faaliyet alanı diye isimlendirilen ayrı bir faaliyet alanı daha tanımlanmıştır. Sınıf faaliyet alanı fonksiyon faaliyet alanı ile dosya faaliyet alanı arasında bir alana sahiptir. Sınıf faaliyet alanı yalnızca bir sınıfın tüm üye fonksiyonları arasında tanınma aralığıdır. Sınıfın veri elelamanları ve üye fonksiyon isimleri sınıf faaliyet alanına uyarlar. Bir sınıfın veri elemanıyla aynı isimli sınıfın üye fonksiyonu içerisinde aynı isimli bir yerel değişken tanımlanabilir. Bu durumda fonksiyon içerisindeki blokta yerel olana erişilir. Benzer biçimde bir üye fonksiyon içerisinde bir fonksiyon çağırılmışsa çağırılan fonksiyon ile aynı isimli hem global hem de bir üye fonksiyon varsa dar faaliyet alanı kuralına göre üye fonksiyon çağırıldığı varsayılır.

Çözünürlük Operatörü(::)(scope resolution operator)

:: operatörüne çözünürlük operatörü denir. Bu opertörün hem binary-infix hem de unary-prefix olarak kullanılan tipleri vardır.

1.   Binay infix resolution operatörü:
Bu kullanımda sol tarafındaki operandın bir sınıf ismi, sağ tarafındaki operandın ise veri elemanı ya da fonksiyon ismi olması gerekir. Bu operatör sınıfın faaliyet alanı probleminden dolayı gizlenmiş olan veri elemanına ya da üye fonksiyonuna erişimini sağlar.

void Sample::fonk1(int a)
{
   printf("Sample fonk1..\n");
   Sample::a = a;         /*sınıfın veri elemanı olan a'ya parametre a'yı ata*/
}

2.   Unary prefix resolution operatörü:
Bu durumda operand global bir değişken ya da fonksiyon ismi olabilir. Bu haliyel bu operatör faaliyet alanı probleminden dolayı global olana erişimi sağlar. Bu operatör öncelik tablosunun en yüksek düzeyinde bulunur.

Başlangıç ve Bitiş Fonksiyonları

1.   Başlangıç Fonksiyonları(constructors)

Bir sınıf destesi tanımlandığında derleyici tarafından otomatik olarak çağırılan fonksiyona sınıfın başlangıç fonksiyonu denir. Yerel bir sınıf nesnesi programın akışı tanımlama noktasına geldiğinde, global bir sınıf nesnesiyse program belleğe yüklenir yüklenmez yaratılır. Başlangıç fonksiyonun  ismi sınıf ismiyle aynı olmalıdır. Başlangıç fonksiyonlarının geri dönüş değeri gibi bir kavramı yoktur. Yani geri dönüş türü yerine bir şey yazılmaz. bu durum int ya da void anlamına gelmez. Başlangış fonksiyonları içerisinde return anahtar sözcüğü kullanılabilir, ancak yanına bir ifade yazılamaz. C++'ta farklı parametre yapısına sahip birden fazla başlangıç fonksiyonu olabilir. Parametresi olmayan(yani void olan) başlangış fonksiyonuna default başlangıç fonksiyonu(default constructor) denir. Eğer sınıf nesnesi nesne isminden sonra parantez açılmadan yani normal bir biçimde tanımlanmış ise (örneğin: X n;) bu durumda varsayılan başlangıç fonksiyonu çağırılır. Eğer nesne isminden sonra bir parantez açılır ve içerisine bir parametre listesi yazılırsa (örneğin: X n(10);) parametre listesine uygun olan başlangıç fonksiyonu çağırılır.
   Uyarı: Nesne isminden sonra parantez açılıp içine hiçbirşey yazılmazsa bu durumda varsayılan başlangıç fonksiyonu çağırılmaz. Bu ifade bir fonksiyon prototipi anlamına gelir. Örneğin:

X a();   /*parametresi olmayan, X türünden bir fonksiyonun prototipi*/

Global sınıf nesnelerine ait başlangıç fonksiyonları main fonksiyonundan önce çağırılır. Daha yukarıda tanımlanan daha önce çağırılacak bir biçimde sıralama söz konusudur.

2.   Bitiş Fonksiyonu(destructor)

Bir nesne faaliyet alanını bitirmesiyle bellekten silinir. Yerel değişkenler programın akışı tanımlandıkları bloğun sonunda, global değişkenler ise programın bitimiyle bellekten silinirler. Bir sınıf nesnesi bellekten silineceği zaman otomatik olarak çağırılan fonksiyona bitiş fonksiyonu(destructor function) denir. Bitiş fonksiyonunun ismi sınıf ismiyle aynıdır, anck başına bir ~ sembolü getirilir. Bitiş fonksiyonunun da geri dönüş değeri gibi bir kavramı yoktur. Bitiş fonksiyonu en az ve en fazla bir tane olabilir. Parametresi void olmak zorundadır. Yani parametresi olmamak zorundadır. Varsayılan bitiş fonksiyonu diye bir kavram yoktur. Global bir sınıf nesnesine ait bitiş fonksiyonu programın sonucunda main bittikten sonra yani main'in sonunda çalıştırılır. Başlangıç ve bitiş fonksiyonlarının çağırılma sıraları her zaman terstir. a ve b herhangi türden iki sınıf nesnesi olmak üzere başlangıç fonksiyonları önce a sonra b olacak şeklinde çağırılıyorsa bitiş fonsiyonları önce b sonra a şeklinde çağırılır(LIFO sistemi).

Başlangıç ve Bitiş Fonksiyolarının Bulundurulma Kuralı

Sınıfın bitiş fonksiyonu olmak zorunda değildir. Yani varsa çağırılır yoksa çağırılmaz. Bir sınıf nesnesinin tanımlanma biçimine uygun bir başlangıç bir fonksiyonu olmak zorundadır. Ancak sınıfın hiçbir başlangıç fonksiyonu yoksa ve nesne varsayılan başlangıç fonksiyonu çağırılacak biçimde tanımlanmışsa bu durum istisna olarak hata oluşturmaz. Ancak sınıfın herhangi bir başlangıç fonksiyonu varsa fakat varsayılan başlangıç fonksiyonu yoksa varsayılan fonksiyonu çağıracak biçimde yapılacak bir tanımlama hata ile sonuçlanır.
Başlangıç ve Bitiş Fonksiyonlarının Kullanılma Nedenleri

Nesne yönelimli programlama da bir sınıf belirli bir amacı gerçekleştiren bir kütüphane olarak ele alınabilir. Örneğin seri port işlemlerini yapan bir sınıf tasarlanabilir. Fare işlemleri için ayrı bir sınıf yazılabilir. Bu sınıfların faydalı işlemleri yapan bir takım üye fonksiyonları olmalıdır. Bu üye fonksiyonlar sınıfın veri elemanlarını ortak olarak kullanırlar. Bir sınıf bir takım yararlı işleri yapmaya aday ise o yararlı işlemleri gerçekleştirmek için bazı hazırlık işlemleri gerekebilir. Örneğin seri port ile ilgili işlem yapan bir sınıfta seri portun set edilmesi, fare işlemleri yapan sınıfta farenin reset edilmesi dosya işlemleri yapan bir sınıfta dosyanın açılması bu tür hazırlık işlemleridir. Bu hazırlık işlemleri sınıfın başlangıç fonksiyonu içerisinde yapılırsa sınıfı kullanan kod küçülür, ayrıntılar göz ardı edilir ve algılama iyileştirilir(abstraction). Örneğin dosya işlemleri yapan sınıfın başlangıç fonksiyonu içerisinde dosya açılabilir. Nesne tanımlanır tanımlanmaz hazırlık işlemlerinin otomatik olarak yapılması sınıfı kullanan kişilerin de işlerini kolaylaştırır.

Bitiş fonksiyonu başlangıç fonksiyonuyla yapılan hazırlık işlemlerinin otomatik bir biçimde geri alınması için kullanılır. Örneğin dosya işlemlerini yapan sınıfın bitiş fonksiyonu otomatik olarak kapayabilir. Seri port işlemlerini yapan sınıfın bitiş fonksiyonu port ayarlarını eski durumuna getirebilir. Tabii bazı durumlarda hazırlık işlemlerinin geri alınması gerekmeyebilir. Yani başlangıç fonksiyonunun olması bitiş fonksiyonunun olmasını mantıksal bakımdan gerekli kılmaz.

Sınıflarda Temel Erişim Kuralları

Temel erişim kuralı sınıf bölümlerinin ne anlama geldiğiyle ilgilidir. İki kural vardır:

1.   Bir sınıf nesnesi yoluyla dışarıdan nokta ya da ok operatörünü kullanarak sınıfın yalnızca public bölümünde bildirilen veri elemanlarına ya da fonksiyonlarına erişilebilir. Private veya protected bölümlerine erişilemez.
2.   Sınıfın üye fonksiyonu hangi bölümde bildirilmiş olursa olsun sınıfın her bölümündeki veri elemanlarına ve üye fonksiyonlarına erişebilir. Yani üye fonksiyonlar içerisinde sınıfın her bölümündeki veri elemanlarını kullanabilir ve üye fonksiyonlarını çağırabiliriz.

Genellikle sınıfın veri elemanları sınıfın rivate bölümünde üye fonksiyonları ise public bölümde tutulur. Böylece veri elemanlarına dışarıdan doğrudan erişilemez. Dışarıdan doğrudan üye fonksiyonlara erişilir. Üye fonksiyonları veri elemanlarına erişirler. Yani veri elemanlarına doğrudan değil üye fonksiyonlar yoluyla erişilmesi istenmiştir. Eğer private bölgedeki veri elemanlarının değerlerini almak ya da bunlara değer yerleştirilmek istenirse bunlarla ilişki kuran bir grup get ve set fonksiyonu yazmak gerekir.

Yeniden kullanılabilirlik(reusability) nesne yönelimli programlama tekniğinin anahtar kavramlarından birisidir. Bu kavram yazılmış olan bir kodun özellikle de bir sınıfın başka projelerde tekrar yazılmadan kullanılması anlamına gelir.

Veri Elemanlarının private, Üye Fonksiyonlarının public Kısmına Yazılması

Genellikle sınıflarda veri koruması istendiği zaman sınıfın veri elemanları private bölgeye üye fonksiyonları ise public bölgeye yazılırlar. Sınıfın veri elemanlarının private bölgeye yerleştirilmesi dışarıdan onlara doğrudan erişimi engeller. Onlara public bölgedeki bir grup üye fonksiyon ile erişiriz. Normalde tasarlanmış olan bir sınıf çok değişik ve uzun kodlarda kullanılabilir. Yani sınıfı kullanan kodlar sınıfın kendi kodlarından çok daha fazladır. Eğer veri elemanlarını private bölgeye yerleştirirsek o veri elemanlarının genel yapısında değişiklik olduğunda sınıfı kullanan kodları değiştirmek zorunda kalmayız. Yalnızca prototipleri aynı kalmak üzere sınıfın üye fonksiyonlarını yeniden yazmak zorunda kalırız. Oysa veri elemanları puıblic bölgeye yerleştirilseydi, dışarıdan bu elemanlara doğrudan erişilebilirdi ve veri yapısı değiştiğinde onu
Yirmi yaşındaki bir insan, dünyayı değiştirmek ister . Yetmiş yaşına gelince , yine dünyayı değiştirmek ister, ama yapamayacağını bilir.

e3

Eklediğin konular için forum adına teşekkür ediyoruz.Ancak yazıların uzun olması sorun yaratacak kanaatindeyim.Yazıları verdiğin gibi linkleri de verirsen kişiler dosyalar kendi bilgisayarlarına kaydederler ve oradan daha rahat okuma imkanı bulurlar.Burada okusa bile nerde kaldığını hatırlamayabilir.Yine de teşekkür ederiz.İyi günler. ;)
.eem.

XX_CİHAN_XX

@protection dediğiniz gibi yapmayı ısterdım fakat bu dosyaları bı kac kaynkatan ındırıp toparladım ve ındırıdıgım lınklerı melesef kaybettım gercı rapidshare e upload edilebilir ama kalıcı olması acısından buraya koymak ıstedım ama tabiiki dediğiniz gibi database alanında ciddi sorun yaratacaksa bu dokumanları bıryerlere upload edelım.
Yirmi yaşındaki bir insan, dünyayı değiştirmek ister . Yetmiş yaşına gelince , yine dünyayı değiştirmek ister, ama yapamayacağını bilir.

e3

Alıntı yapılan: "XX_CİHAN_XX"@protection dediğiniz gibi yapmayı ısterdım fakat bu dosyaları bı kac kaynkatan ındırıp toparladım ve ındırıdıgım lınklerı melesef kaybettım gercı rapidshare e upload edilebilir ama kalıcı olması acısından buraya koymak ıstedım ama tabiiki dediğiniz gibi database alanında ciddi sorun yaratacaksa bu dokumanları bıryerlere upload edelım.

Hemen paylaş içerikleri legal olmayan dosyaları silmiyor diye biliyorum.Neyse böyle kalsınlar.Bende Temel Elektronik bölümünde eklediğim konuları forumda kalıcı olsun diye metin halinde yazıyorum.İyi günler.Saygılar.
.eem.

argeelektronik

Altında Çürümekten Kurtulamıyacağın Topraklar Üzerinde Böbürlenerek Yürüme, Üzerine Tükürmeye Bile Miğdenin kaldıramıyacağı Kurtçuklara Yem Olacak Bedeninle...