FBEB-512 C++ ile Nesne Tabanlı Programlama Güz 2009 (4. Hafta) (Yrd. Doç. Dr. Deniz Dal)
Hata Ayıklama (Debugging) Yazdığımız programlar ya hiç çalıģmayabilir ya da çalıģır ama beklediğimiz sonuçları üretmez. ÇALIġAN AMA BEKLEDĠĞĠMĠZ GĠBĠ SONUÇLAR ÜRETMEYEN programlarımızdaki MANTIKSAL hataları bulmak için Visual Studio programının DEBUG menüsü seçeneklerinden faydalanırız. Debug Menüsü -> Run To Cursor (Ctrl+F10) : Ġmlecin bulunduğu satırın öncesindeki bütün satırlar Visual Studio tarafından iģletilir ve kontrol imlecin olduğu satırda kullanıcıya devredilir. Debug Menüsü -> Step Over (F10) : Kontrolü devralan kullanıcı F10 tuģu yardımıyla programın kalan satırlarını manuel olarak iģletir. F10 tuģu fonksiyonların içerisine dallanmaz. Debug Menüsü -> Step Into (F11) : F11 tuģu yardımıyla fonksiyonların içerisine de girilir. Programlarımızdaki mantıksal hataları tespit edebilmenin bir baģka yolu da hatanın kaynaklanabileceğini düģündüğümüz noktanın (noktaların) öncesine veya sonrasına cout deyimleri eklemektir.
Fonksiyonlar (Functions) Matematik Kütüphanesi Fonksiyonları Fonksiyon Tanımı Fonksiyon Prototipleri Standart Kütüphaneden Header Dosyaları Recursive (Tekrarlamalı) Fonksiyonlar Değer ile ya da Referansla Fonksiyon Çağırma Fonksiyonların AĢırı Yüklenmesi Fonksiyon Kalıpları
Fonksiyonlara GiriĢ En iyi program yazma ve geliģtirme pratiği tek bir büyük program yazmak yerine onu daha kolay kontrol altında tutmamıza yardımcı olabilecek küçük programların birleģimi halinde oluģturmaktır. Bu yönteme Divide&Conquer ( Böl&Yönet ) adı verilir. Bir fonksiyon gerektiğinde fonksiyon adı ile çağrılır ve fonksiyonun iģleyeceği bilgiler argüman (parametre) olarak fonksiyona yerleģtirilir. Örneğin bir patron (fonksiyonu çağıran) bir çalıģanını (çağrılan fonksiyon) yanına çağırır ve doldurması için eline bir dosya verir (argüman). ÇalıĢanı iģini bitirdiği zaman iģlenmiģ dosyayı patronuna geri götürür (return).
Matematik Kütüphanesi Fonksiyonları Bu fonksiyonlar standart kütüphaneden <cmath> header dosyası include edilerek kullanılabilir. Örnek: cout<<sqrt(900.0); Bu örnekte matematik kütüphanesi fonksiyonlarından sqrt çağrılır. 900.0 rakamı bu fonksiyonun argümanı olarak adlandırılır. Yukarıdaki deyim ekrana sonuç olarak 30.0 yazdırır. sqrt fonksiyonu kendisine double veri tipinde bir argüman alır ve yine double veri tipinde bir değer döndürür. Matematik kütüphanesinin bütün fonksiyonları geriye double veri tipinde bir değer döndürürler. Fonksiyon argümanları sabit bir sayı, değiģken ya da bunların karması olabilir.
Matematik Kütüphanesi Fonksiyonları FONKSİYON TANIM ÖRNEK ceil( x ) cos( x ) x argümanını kendinden büyük ilk tamsayıya yuvarlar. Radyan cinsinden x argümanına kosinüs fonksiyonunu uygular. ceil( 9.2 ) is 10.0 ceil( -9.8 ) is -9.0 cos( 0.0 ) is 1.0 exp( x ) Exponansiyel Fonksiyon e x exp( 1.0 ) is 2.71828 exp( 2.0 ) is 7.38906 fabs( x ) floor( x ) fmod( x, y ) x argümanının mutlak değerini hesaplar. x argümanını kendinden küçük ilk tamsayıya yuvarlar. x sayısının y sayısına bölümünden kalan ondalıklı sayıyı hesaplar. log( x ) Doğal logaritma hesaplar. (e tabanında) fabs( 5.1 ) is 5.1 fabs( 0.0 ) is 0.0 fabs( -8.76 ) is 8.76 floor( 9.2 ) is 9.0 floor( -9.8 ) is -10.0 fmod( 2.6, 1.2 ) is 0.2 log( 2.718282 ) is 1.0 log( 7.389056 ) is 2.0 log10( x ) 10 tabanında logaritma hesaplar. log10( 10.0 ) is 1.0 log10( 100.0 ) is 2.0 pow( x, y ) sin( x ) sqrt( x ) tan( x ) pow( 2, 7 ) is 128 x argümanının y. kuvvetini hesaplar.x y pow( 9,.5 ) is 3 Radyan cinsinden x argümanına sinüs fonksiyonunu uygular. sin( 0.0 ) is 0 Pozitif x argümanının karekökünü hesaplar. Radyan cinsinden x argümanına tanjant fonksiyonunu uygular. sqrt( 9.0 ) is 3.0 tan( 0.0 ) is 0
Fonksiyonlar Nasıl Çağrılır? 1. #include <iostream> //cout ve cin kullanimi icin 2. using namespace std; 3. int KaresiniAl(int); //fonksiyon prototipi veya imzası (signature) 4. int main() 5. { 6. for(int i=1;i<=10;i++) 7. cout<<karesinial(i)<< ; 8. cout<<endl; 9 return 0; 10. } 11. //fonksiyon tanimi 12. int KaresiniAl(int y) 13. { 14. return y*y; 15. }
Fonksiyon Formatı döndürülen veri tipi fonksiyonun adı (parametre listesi) { deklarasyonlar ve deyimler; return anahtar kelimesi; } Parametre listesindeki birden fazla parametre birbirlerinden virgül ile ayrılır. Herbir parametre fonksiyon içerisinde bir değiģken olarak kullanılacağı için bir isme ve bir veri tipine sahiptir. Döndürülen veri tipi void ise bu fonksiyon hiçbir veri geri döndürmez ve return anahtar kelimesi kullanılmak zorunda değildir.
Void Fonksiyonlar void Print(); adlı fonksiyon deklarasyonu Print adlı fonksiyonun hiçbir argüman almadığını ve hiçbir değer geri döndürmediğini söyler bize. #include <iostream> //cout kullanimi icin using namespace std; void Print(); //fonksiyon prototipi int main() { Print(); return 0; //program sorunsuz sona erdiyse } //fonksiyon tanimi void Print() { cout<< Bu fonksiyon hicbir arguman almaz ve hicbirsey geri cevirmez.\n ; }
Paskal Notasyonu Fonksiyonlar isimlendirilirken en fazla kullanılan metodlardan biri Paskal notasyonudur (pascal notation). Bu notasyon, fonksiyon ismi birden fazla kelime içeriyorsa ilk kelime de dahil bütün kelimelerin ilk karakterlerinin büyük harfle yazıldığı bir notasyondur. KaresiniAl, AsalMi, Swap gibi.
Fonksiyon Prototipi Fonksiyon prototipi, derleyiciye, kullanılacak fonksiyonun adını, döndürülecek veri tipini, kaç parametre alınacağını ve tiplerini, bu parametrelerin hangi sırada olacağını hatırlatır. Parametre olarak kullanılan değiģkenlerin isimleri prototip içine yazılmaz. Sadece değiģkenlerin veri tipleri yazılır. Eğer fonksiyon prototipte tanımlandığı gibi çağrılmazsa derleyici hata verir. Bütün bunlar fonksiyon kullanımını daha da kolaylaģtırmak ve yapılacak yanlıģları önlemek içindir. Eğer fonksiyon, çağrılmadan önce tanımlanmıģsa prototipe gerek yoktur. Sonra tanımlanmıģsa gerek vardır. Örneğin: int Maksimum(int,int,int); prototipi Maksimum adlı fonksiyonun 3 integer parametre alacağını ve geriye yine bir integer döndüreceğini söyler.
Header Dosyaları Standart kütüphanenin her bir elemanı, bünyesinde barındırdığı fonksiyonların prototiplerini ve tanımlarını header file dediğimiz ve include deyimi ile programımızın baģına eklediğimiz dosyaların içinde saklar. Standart kütüphaneden eklediğimiz header dosyaları < ile > karakterleri arasına konur. Kullanıcının oluģturduğu header dosyaları ise ile karakterleri arasına yerleģtirilir ve uzantısı.h dir. Örnek : #include <iostream> #include fbeb512.h
C++ Standart Kütüphanesinin Header Dosyaları C++ Standard Library Header File <iostream> Açıklama Standart input ve output işlemleri için gerekli fonksiyon prototiplerini ve tanımlarını içerir. Mesela cout ve cin kullanabilmek için bu header dosyası gereklidir. <iomanip> Veriyi formatlı yazdırabilmek için gerekli fonksiyon prototiplerini ve tanımlarını içerir. Mesela setprecision kullanabilmek için bu header dosyası gereklidir. <cmath> <cstdlib> Matematik kütüphanesinde tanımlı fonksiyonların prototiplerini ve tanımlarını içerir. (pow, ceil veya floor gibi) Rakamları metinlere, metinleri rakamlara çeviren fonksiyonların ve rastgele sayı üreten fonksiyonların prototiplerini ve tanımlarını içerir. (atoi, atof, rand veya srand gibi)
C++ Standart Kütüphanesinin Header Dosyaları C++ Standard Library Header File <ctime> <vector>, <list>, <deque>, <queue>, <stack>, <map>, <set>, <bitset> <cctype> <cstring> Açıklama Zaman ve tarih manipülasyonu için kullanılan fonksiyonların prototiplerini ve tanımlarını içerir. (time gibi) Bunlar C++ standart kütüphanesi içinde depolama amaçlı kullanılan sınıfların (classes) header dosyalarıdır. Karakterlere ve metin dizilerine uygulanabilecek testleri içeren fonksiyonların prototiplerini ve tanımlarını içerir. Mesela bir karakterin harf mi veya noktalama işareti mi olduğunu bulmamıza yarayan bir fonksiyon bu header dosyası eklenerek kullanılabilir. (Ya da küçük harfleri büyük harflere çeviren ya da tam tersi.) (isdigit, islower, tolower veya toupper gibi) C programlama dilinin stiline uygun string proses etmek için kullanılacak fonksiyonların prototiplerini ve tanımlarını içerir. (strcpy veya strncmp gibi)
C++ Standart Kütüphanesinin Header Dosyaları C++ Standard Library Header File Açıklama <fstream> <string> <sstream> Hard diskteki bir dosyadan bilgi okuma veya dosyaya bilgi yazma amacıyla kullanılacak fonksiyonların prototiplerini ve tanımlarını içerir. C++ Standart kütüphanesinde tanımlı string sınıfının (class) tanımını içerir. Bellekteki stringlerin okunması ve yazılması için gerekli fonksiyonların prototiplerini ve tanımlarını içerir. <functional> C++ Standart kütüphanesinde mevcut algoritmalara ait sınıfların ve fonksiyonların tanımlarını içerir.
C++ Standart Kütüphanesinin Header Dosyaları C++ Standard Library Header File <iterator> <algorithm> Açıklama C++ kütüphanesindeki depolayıcıların içindeki verilere ulaşmak için gerekli sınıf tanımlamalarını içerir. C++ kütüphanesindeki depolayıcıların içindeki verileri manipule etmek için gerekli fonksiyonları içerir. (find, count veya swap gibi)
Rastgele integer Sayı Üretme Standart kütüphaneden <cstdlib> include edilir ve burada tanımlı rand() fonksiyonu bu iģ için kullanılır. rand() fonksiyonu 0 ile RAND_MAX arasında rastgele bir tamsayı üretir. RAND_MAX cstdlib kütüphanesi içerisinde tanımlı sabit bir sayıdır ve değeri Ģu an itibariyle 32767 dir. rand() fonksiyonunun ürettiği rastgele sayı istenilen aralığa modülüs(%) operatörü kullanılarak çekilir. Örneğin rand()%6 bize 0 ile 5 arasında integer sayılar üretir. Örneğin rand()%6+1 bize 1 ile 6 arasında integer sayılar üretir. rand() fonksiyonu programımızı test (debug) ederken bize kolaylık olsun diye programı çalıģtırdığımız her sefer aynı sayıyı üretir.
Soru... 32 ile 122 arasında rastgele bir tamsayı üretecek C++ deyimi aģağıdakilerden hangisidir? a) rand()%122+32 b) rand()%32+122 c) rand()%90+32 d) rand()%32+90 e) Hocam!! Bu tür sorularla aklımızı karıģtırmayınız
srand() Fonksiyonu Programımızı test ettikten ve tam olarak çalıģtığına emin olduktan sonra programın çalıģtırıldığı her sefer farklı bir sayı üretilsin isteriz. Bunun için yine standart kütüphanenin <cstdlib> header dosyasından srand fonksiyonunu kullanırız. srand fonksiyonu kendisine unsigned int veri tipine sahip bir argüman alır. Bu argüman çekirdek değer(seed) olarak adlandırılır. Her seferinde farklı bir çekirdek değer girmek yerine srand fonksiyonuna argüman olarak standard kütüphanenin <ctime> header dosyasının içinde tanımlı time fonksiyonu kullanılır. Örneğin time(0) (time fonksiyonu sıfır değerini kendisine argüman olarak almıģ) bilgisayarın o anki saat değerini saniye cinsinden geri döndürür. srand(time(0)) ise üretilen bu saat değerini unsigned integer e çevirir ve çekirdek değer olarak kullanır.
Örnek: rand() Fonksiyonu #include <iostream> //cout kullanimi icin #include <cstdlib> //rand() fonksiyonu icin using namespace std; int main() { } cout<< Uretilebilecek Maximum Sayi: <<RAND_MAX<<endl; for(int i=1;i<=20;i++) cout<<(1+rand()%6)<<endl; return 0;
Örnek: srand(seed) Fonksiyonu #include <iostream> //cout kullanimi icin #include <cstdlib> //rand() ve srand(seed) fonksiyonu icin using namespace std; int main() { unsigned int seed; cout<< Cekirdek degeri girin : ; cin>>seed; srand(seed); } for(int i=1;i<=20;i++) cout<<(1+rand()%6)<<endl; return 0;
Örnek: srand(time(0)) Fonksiyonu #include <iostream> //cout kullanimi icin #include <cstdlib> //rand() ve srand(seed) fonksiyonu icin #include <ctime> //time() fonksiyonu icin using namespace std; int main() { srand(time(0)); //programin calistigi andaki saat degeri cout<< Saatin Tam Su Anki Degeri: <<time(0)<<endl; for(int i=1;i<=20;i++) cout<<(1+rand()%6)<<endl; return 0; }
Recursive (Rikörsif) (Tekrarlamalı) Fonksiyonlar ġu ana kadar gördüğümüz ve tanımladığımız fonksiyonlarda hiyerarģik bir yapı içerisinde bir fonksiyon baģka bir fonksiyonu çagırıyordu. Bazı problemler icin fonksiyonların kendi kendilerini çagırmaları daha faydalı olabilir. Bu tür fonksiyonlara Recursive (Rikörsif) (tekrarlamalı) fonksiyonlar denir. Bir rikörsif fonksiyonun ne zaman kendi kendini çağırmayı durduracağını bilmesi gerekir. Biz buna temel durum (base case) diyoruz. Eğer bu Ģart sağlanmazsa program sonsuza kadar çalıģır. Öte yandan rikörsif fonksiyonlarla gerçekleģtirilebilen iģlemler döngüler ile de yapılabilirler.
Örnek: Rikörsif Faktoriyel Hesaplama #include <iostream> using namespace std; int Faktoriyel(int); //fonksiyon prototipi int main() { for(int i=0;i<=10;i++) cout<<i<<! <<Faktoriyel(i)<<endl; return 0; //program sorunsuz sona erdiyse } //Rikörsif faktoriyel hesaplama fonksiyonu tanimi int Faktoriyel(int sayi) { if(sayi <=1)//temel durum return 1; else //recursive durum return sayi*faktoriyel(sayi-1); }
5!
Değer ya da Referans ile Fonksiyon Çağırma Birçok bilgisayar dilinde argümanlar fonksiyonlara iki Ģekilde iletilirler. Değer ile (call-by-value) ya da referans ile (call-byreference). Değer ile bir argüman bir fonksiyona verildiğinde argümanın bir kopyası alınır ve fonksiyon içinde değerlendirilir. Fonksiyon içinde argüman değerine yapılan değiģiklikler fonksiyonun çağrıldığı yerdeki değiģkeni etkilemez. (ORJĠNAL EVRAKIN FOTOKOPĠSĠ) Bu sunuda Ģu ana kadar gördüğümüz örneklerde güvenli olduğu için hep bu yolu kullandık. Ama bazı durumlarda referans ile fonksiyon çağırmak icap eder. Bu yolla, fonksiyonu çağıran, fonksiyonun içinde gönderdiği argümanın değiģtirilmesine de izin vermiģ olur. (ORJĠNAL EVRAK) Bu tür bir fonksiyona en güzel örnek swap fonksiyonudur. Referans parametresi ya da argümanı ile ilgilendiğimizi göstermek için & karakterini kullanırız.
ĠKĠ DEĞĠġKENĠN DEĞERĠNĠN YER DEĞĠġTĠRMESĠ (SWAPPING) A B Elinizdeki TV kumandası yardımıyla kayıtlı 2 kanalın yerini nasıl değiģtirirsiniz? gecicidegisken=a; A=B; B=geciciDegisken;!!! GEÇĠCĠ BĠR DEĞĠġKENE ĠHTĠYAÇ VAR!!!
Değer ile Swap Fonksiyonu (ÇalıĢmaz) #include <iostream> using namespace std; void Swap(int,int); //fonksiyon prototipi int main() { int a=2,b=5; cout<< Swap Fonksiyonu Cagrilmadan Once:\n ; cout<< a= <<a<< b= <<b<<endl; Swap(a,b); cout<< Swap Fonksiyonu Cagrildiktan Sonra:\n ; cout<< a= <<a<< b= <<b<<endl; return 0; } //Fonksiyon tanimi void Swap(int a, int b) { int gecicidegisken=a ; a=b; b=gecicidegisken; }
Referans ile Swap Fonksiyonu (ÇalıĢır) #include <iostream> using namespace std; void Swap(int&,int&); //fonksiyon prototipi int main() { int a=2,b=5; cout<< Swap Fonksiyonu Cagrilmadan Once:\n ; cout<< a= <<a<< b= <<b<<endl; Swap(a,b); cout<< Swap Fonksiyonu Cagrildiktan Sonra:\n ; cout<< a= <<a<< b= <<b<<endl; return 0; } //Fonksiyon tanimi void Swap(int &a, int &b) { int gecicidegisken=a ; a=b; b=gecicidegisken; }
Soru Fonksiyonlar return komutuyla sadece bir değer geriye çevirirler. Geriye birden fazla değer çevirebilmek için ne yapmamız gerekir?
Referans ile DeğiĢken Kontrolü Referanslar baģka değiģkenlerin yerine takma isim (alias) olarak da kullanılabilirler. Alias (orijinal değiģkenin baģka bir adı olarak da düģünülebilir) olarak tanımlanan bir değiģken üzerinde yapılan her türlü iģlem orjinal değiģken uzerinde de gerçekleģir. Alias değiģkenler MUHAKKAK ilk değerli (initialization) olarak deklare edilmelidirler. Örnek: int count = 1; int &ref = count; ref++; count değişkeninin değeri ref adlı alias kanalıyla bir artırılır.
Fonksiyon AĢırı Yüklemesi (Function Overloading) C++ aynı fonksiyon adına sahip birden fazla fonksiyonun kullanımına izin verir. Ancak bu durum sadece aynı adlı fonksiyonların farklı argüman listelerine ya da dönüģ değerlerine sahip olması durumunda geçerlidir. C++ derleyicisi aģırı yüklenmiģ bir fonksiyon çağrıldığında fonksiyonun kaç argümana sahip olduğuna, argüman veri tiplerine ve argümanların sırasına bakarak uygun fonksiyonu çalıģtırır. Fonksiyon aģırı yüklemesi, genellikle, benzer iģleri farklı veri tipleri üzerinde yapan aynı ada sahip fonksiyon tanımları için kullanılır. Örnek: int KaresiniAl(int x){return x*x;} double KaresiniAl(double y){return y*y;}
Fonksiyon Kalıpları (Function Templates) Fonksiyon kalıpları kullanarak aynı iģlemi farklı veri tipleri üzerinde yapan fonksiyonlar için birden fazla fonksiyon tanımı yapmak zorunda kalmayız. Fonksiyon kalıpları template anahtar kelimesi ile baģlar ve veri tipleri class anahtar kelimesi ile < ve > karakterleri arasında tanımlanır.
Fonksiyon Kalıpları (Function Templates) #include <iostream> using namespace std; template <class T> //Fonksiyon Prototipi T Maximum(T,T,T); int main() { int int1=5,int2=7,int3=17; Maximum(int1,int2,int3); double double1=7.32,double2=13.45,double3=21.56; Maximum(double1,double2,double3); return 0; } template <class T> //Fonksiyon Tanimi T Maximum(T value1,t value2,t value3) { T max=value1; if(value2>max) max=value2; if(value3>max) max=value3; return max; }