C

Programlarımızın kolay yazılır, doğru, bakımı kolay, ve belli sınırlar içerisinde verimli olmalarını isteriz. Bundan, doğal olarak C++'ı (ve başka dilleri de) bu amaca olabildiğince yakın olarak kullanmamız gerekliliği ortaya çıkar. C++ kullananların hâlâ Standart C++'ın getirdiği olanakları benimsemeyerek, C++ kullanım biçemlerini değiştirmediklerine; ve bu nedenle, değindiğim amaca yönelik büyük adımların atılamadığına inanıyorum. Bu yazı, Standart C++'ın getirdiği olanakların değil, bu olanakların desteklediği programlama biçemlerinin üzerinde durmaktadır.
Büyük gelişmelerin anahtarı, kod büyüklüğünü ve karmaşıklığını kitaplıklar kullanarak azaltmaktır. Bunları aşağıda, C++'a giriş kurslarında karşılaşılabilecek birkaç örnek kullanarak gösteriyor ve nicelendiriyorum.
Kod büyüklüğünü ve karmaşıklığını azaltarak hem geliştirme zamanını azaltmış oluruz, hem program bakımını kolaylaştırırız, hem de program sınamanın bedelini düşürürüz. Daha da önemlisi, C++'ın öğrenilmesini de kolaylaştırmış oluruz. Önemsiz veya bir dersten yalnızca geçer not almak için yazılan programlarda bu basitleştirme yeterlidir. Ancak, verimlilik, profesyonel programcılar için çok önemli bir konudur. Programlama biçemimizi, ancak günümüz bilişim hizmetlerinde ve işyerlerinde karşılaşılan boyutlardaki verilerle ve gerçek zaman programlarıyla uğraşırken verimlilik kaybına neden olmayacaksa değiştirebiliriz. Onun için, karmaşıklığın azaltılmasının verimliliği düşürmeden elde edilebileceğini gösteren ölçümler de sunuyorum.
Son olarak, bu görüşün C++'ın öğrenilmesi ve öğretilmesine olan etkilerini tartışıyo
KARMAŞIKLIK
"Lütfen adınızı girin" yazın
adı okuyun
"Merhaba " yazın
Standart C++ çözümü şöyledir:
#include // standart dizgi
{
using namespace std; // standart kitapliga erisim
string ad;
cin >> ad;
cout << "Merhaba " << ad << 'n';
}
{
const int encok = 20;
char ad[encok];
scanf("%s", ad); // ady oku
printf("Merhaba %sn", ad);
}
Daha sonradan uygun bir çözüm gösterildiği sürece bu niteliksizliğin zararsız olduğu öne sürülebilir. Ancak bu ifade "iyi" olmak yerine, olsa olsa "kabul edilebilir" olabilir. Yeni bir programcıya bu kadar kırılgan bir program göstermemek çok daha iyidir.
Peki davranış olarak C++ eşdeğerine yakın bir C programı nasıl olurdu? İlk deneme olarak dizi taşmasını 'scanf()'i daha doğru kullanarak engelleyebilirdik:
{
const int encok = 20;
char ad[encok];
scanf("%19s", ad); // adi en fazla 19 harf olarak oku
printf("Merhaba %sn", ad);
}
sprintf(bicim, "%%%ds", encok-1); // bicim dizgisini hazirla; %s tasabilecegi icin
scanf(bicim, ad);
#include
#include
{
fprintf(stderr, "Bellekte yer kalmadin");
exit(1);
}
{
int encok = 20;
char * ad = (char *)malloc(encok); // arabellek ayir
if (ad == 0) cik();
int c = getchar();
if (c == EOF) break; // kutuk sonu
if (!isspace(c)) {
ungetc(c,stdin);
break;
}
}
while (true) {
int c = getchar();
if (c == 'n' || c == EOF) { // sonlandirma karakterini ekle
ad[i] = 0;
break;
}
ad[i] = c;
if (i==encok-1) { // arabellek doldu
encok = encok+encok;
ad = (char*)realloc(ad,encok); // daha buyuk yeni bir arabellek ayir
if (ad == 0) cik();
}
i++;
}
free(ad); // arabellegi birak
return 0;
}
Bu örneğin o kadar da kötü olmadığı öne sürülebilir. Zaten birçok deneyimli C ve C++ programcısı gerçek bir programda herhalde (umarız?) buna benzer birşey yazmıştır. Daha da ileri giderek, böyle bir programı yazamayacak birisinin profesyonel bir programcı olmaması gerektiğini bile ileri sürebiliriz. Bu programın yeni başlayan birisini ne kadar zorlayacağını düşünün. Program bu şekliyle dokuz değişik standart kitaplık işlevi kullanmakta, oldukça ayrıntılı karakter düzeyinde giriş işlemleriyle uğraşmakta, işaretçiler kullanmakta, ve bellek ayırmayla ilgilenmektedir. Hem 'realloc()'u kullanıp hem de uyumlu kalabilmek için 'malloc()'u kullanmak zorunda kaldım ('new'ü kullanmak yerine). Bunun sonucu olarak da işin içine bir de arabellek boyutları ve tür dönüşümleri girmiş oldu. (C'nin bunun için tür dönüşümünü açıkça yazmayı gerektirmediğini biliyorum. Ama onun karşılığında ödenen bedel, 'void *'dan yapılan güvensiz bir örtülü tür dönüşümüne izin vermektir. Onun için C++, böyle bir durumda tür dönüşümünün açıkça yapılmasını gerektirir.) Bellek tükendiğinde tutulacak en iyi yolun ne olduğu bu kadar küçük bir programda o kadar açık değil. Konuyu fazla dallandırmamak için kolay anlaşılır bir yol tuttum. C biçemini kullanan bir öğretmen, bu konuda ilerisi için temel oluşturacak ve kullanımda da yararlı olacak uygun bir yol seçmelidir.
Özetlersek, başta verdiğimiz basit örneği çözmek için, çözümün özüne ek olarak, döngüleri, koşulları, bellek boyutlarını, işaretçileri, tür dönüşümlerini, ve bellek yönetimini de tanıtmak zorunda kaldım. Bu biçemde ayrıca hataya elverişli birçok olanak da var. Uzun deneyimimin yardımıyla bir eksik, bir fazla, veya bellek ayırma hataları yapmadım. Ama bir süredir çoğunlukla C++'ın akım giriş/çıkışını kullanan birisi olarak, yeni başlayanların çokça yaptıkları hatalardan ikisini yaptım: 'int' yerine 'char'a okudum ve EOF'la karşılaştırmayı unuttum. C++ standart kitaplığının bulunmadığı bir ortamda çoğu öğretmenin neden düşük nitelikli çözümü yeğleyip bu konuları sonraya bıraktığı anlaşılıyor. Ne yazık ki çoğu öğrenci düşük nitelikli biçemin "yeterince iyi" olduğunu ve ötekilerden (C++ olmayan biçemler içinde) daha çabuk yazıldığını hatırlıyor. Sonuçta da vazgeçilmesi güç bir alışkanlık edinip arkalarında yanlışlarla dolu programlar bırakıyorlar.
İşlevsel eşdeğeri olan C++ programı 10 satırken, son C programı tam 41 satır. Programların temel öğelerini saymazsak fark, 30 satıra karşın 4 satır. Üstelik C++ programındaki satırlar hem daha kısa, hem de daha kolay anlaşılır. C++ ve C programlarını anlatmak için gereken toplam kavram sayısını ve bu kavramların karmaşıklıklarını nesnel olarak ölçmek zor. Ben C++ biçeminin 10'a 1 daha kazançlı olduğunu düşünüyorum.
VERİMLİLİK
Verimliliğin önemli olduğu sistemlerde bulunabilecek türden basit bir örneği ele alalım:
belirsiz sayıda öğe oku
öğelerin her birisine bir şey yap
öğelerin hepsiyle bir şey yap
Aklıma gelen en basit örnek, girişten okunacak bir dizi çift duyarlıkla kayan noktaya sanyınyan ortalama ve orta değerlerini bulmak. Bunun geleneksel C gibi yapılan bir çözümü böyle olurdu:
#include
{
register double p0 = *(double*)p; // sayilari karsilastir
register double q0 = *(double*)q;
if(p0>q0) return 1;
if (p0 return 0;
}
{
fprintf(stderr, "Bellekte yer kalmadin");
exit(1);
}
{
int boyut = 1000; // ayrimin baslangic boyutu
char* kutuk = argv[2];
if (arabellek==0) cik();
double ortalama = 0;
int adet = 0; // toplam öge sayisi
double sayi;
while(fscanf(giris, "%lg", &sayi) == 1) { // sayiyi oku, ortalamayi deðistir
if (adet==boyut) {
boyut += boyut;
arabellek = (double*)realloc(arabellek, sizeof(double)*boyut);
if (arabellek==0) cik();
}
arabellek[adet++] = sayi;
// olasi yuvarlatma hatasi:
ortalama = (adet==1) ? sayi : ortalama + (sayi - ortalama) / adet;
}
int ortadaki = adet / 2;
orta = (adet % 2) ? arabellek[ortadaki] : (arabellek[ortadaki - 1] + arabellek[ortadaki]) / 2;
}
}
#include
#include
#include
{
char * kutuk = argv[2];
vector arabellek;
double ortalama = 0;
double sayi;
while (giris >> sayi) {
arabellek.push_back(sayi);
// olasý yuvarlatma hatasý:
ortalama = (arabellek.size() == 1) ? sayi : ortalama + (sayi - ortalama) / arabellek.size();
}
int ortadaki = arabellek.size() / 2;
orta = (arabellek.size() % 2) ? arabellek[ortadaki] : (arabellek[ortadaki-1]+arabellek[ortadaki]) / 2;
}
<< ", orta deger = " << orta << ", ortalama = " << ortalama << 'n';
}
Tekrar belirtirsem, bellek yönetimi C++ programında örtülü olarak yapılıyor; yeni öğeler 'push_back'le eklendikçe 'vector' gerektiğinde kendiliğinden büyüyor. C gibi yazılan programda bu işin 'realloc()' kullanılarak açıkça yapılması gerekir. Aslında, C++ programında kullanılan 'vector'ün kurucu ve 'push_back' işlevleri, C gibi yazılan programdaki 'malloc()', 'realloc()', ve ayrılan belleğin büyüklüğüyle uğraşan kod satırlarının yaptıkları işleri örtülü olarak yapmaktadırlar. C++ gibi yazılan programda belleğin tükenme olasılığını C++'ın kural dışı durum işleme düzeneğine bıraktım. Belleğin bozulmasını önlemek için C gibi yazılan programda bunu açıkça yazdığım sınamalarla yaptım.
C++ programına doğru olarak oluşturmak da daha kolaydı. İşe bazı satırları C gibi yazılan programdan kopyalayarak başladım. kütüğünü içermeyi unuttum, iki yerde 'adet'i 'arabellek.size()'la değiştirmeyi unuttum, derleyicim yerel 'using' yönergelerini desteklemediği için 'using namespace std;' satırına 'main()'in dışına taşıdım. Program bu dört hatayı düzeltmemin ardından hatasız olarak çalıştı.
Programlamaya yeni başlayanlar 'qsort()'u biraz "garip" bulurlar. Öğe sayısının belirtilmesi neden gereklidir? (Çünkü C dizileri bunu bilmezler.) 'double'ın büyüklüğünün belirtilmesi neden gereklidir? (Çünkü 'qsort()' 'double'ları sıralamakta olduğunu bilmez.) O hoş gözükmeyen 'double' karşılaştırma işlevini neden yazmak zorundayız? (Çünkü 'double' sıraladığını bilmeyen 'qsort()'a karşılaştırmayı yaptırması için bir işlev gerekir.) 'qsort()'un kullandığı karşılaştırma işlevinin bağımsız değişkenleri neden 'char*' türünde değil de 'const void*' türündedir? (Çünkü 'qsort()'un sıralaması dizgi olmayan türden değişkenler üzerinedir.) 'void*' nedir ve 'const' olmasa ne anlama gelir? (E, şey, buna daha sonra değineceğiz.) Bunu yeni başlayanın boş bakışlarıyla karşılaşmadan anlatmak oldukça zordur. Bununla karşılaştırıldığında, sort(v.begin(), v.end())'in ne yaptığını anlatmak çok kolay: "Bu durumda 'sort(v)' kullanmak daha kolay olurdu ama bazen bir kabın yalnızca bir aralığındaki öğeleri sıralamak istediğimiz için, daha genel olarak, sıralanacak aralığın başını ve sonunu belirtiriz."
Programların verimliliklerini karşılaştırmadan önce, bunu anlamlı kılmak için kaç tane öğe kullanılması gerektiğini belirledim. Öğe sayısı 50.000 olduğunda programların ikisi de işlerini yarım saniyenin altında bitirdiler. Onun için programları 500.000 ve 5.000.000 öğeyle çalıştırdım.
Kayan noktalı sayıları okumak, sıralamak, ve yazmak
|
||||||
|
Eniyileştirmeden
|
Eniyileştirerek
|
||||
|
C++
|
C
|
C/C++ oranı
|
C++
|
C
|
C/C++ oranı
|
500.000 öğe
|
3.5
|
6.1
|
1.74
|
2.5
|
5.1
|
2.04
|
5.000.000 öğe
|
38.4
|
172.6
|
4.49
|
27.4
|
126.6
|
4.62
|
C++ gibi yazılan programların çok az farkla daha hızlı çıkacaklarını bekliyordum. Ama başka gerçeklemeler kullandığım zaman sonuçlarda büyük oynamalar gördüm. Hatta bazı durumlarda, küçük sayıda öğeler kullanıldığında, C gibi yazılan program C++ gibi yazılan programdan daha hızlı çıktı. Ancak, bu örneği kullanarak, üst düzey soyutlamaların ve hatalara kaşı daha iyi korunmanın günümüz teknolojisiyle kabul edilir hızlarda elde edilebileceğini göstermeye çalıştım. Salt araştırma konusu olmayan, yaygın, ve ucuz olarak elde edilebilen bir gerçekleme kullandım. Daha da yüksek hızlara ulaştığını söyleyen gerçeklemeler de var.
Kolaylık elde etmek ve hatalara karşı daha iyi korunmak için; 3, 10, hatta 50 kat fazla ödemeyi kabul eden insanlar bulmak güç değildir. Bunlara ek olarak iki kat, dört kat gibi bir hız kazancı da olağanüstü. Bence bu değerler, bir C++ kitaplık firması için kabul edilir en düşük değerler olmalıdır.
Programların kullandıkları sürenin nerelerde geçirildiğini anlamak için birkaç deneme daha yaptım:
500.000 öğe
|
||||||
|
Eniyileştirmeden
|
Eniyileştirerek
|
||||
|
C++
|
C
|
C/C++ oranı
|
C++
|
C
|
C/C++ oranı
|
okuma
|
2.1
|
2.8
|
1.33
|
2.0
|
2.8
|
1.40
|
üretme
|
.6
|
.3
|
.5
|
.4
|
.3
|
.75
|
okuma-sıralama
|
3.5
|
6.1
|
1.75
|
2.5
|
5.1
|
2.04
|
üretme-sıralama
|
2.0
|
3.5
|
1.75
|
.9
|
2.6
|
2.89
|
5.000.000 öğe
|
||||||
|
Eniyileştirmeden
|
Eniyileştirerek
|
||||
|
C++
|
C
|
C/C++ oranı
|
C++
|
C
|
C/C++ oranı
|
okuma
|
21.5
|
29.1
|
1.35
|
21.3
|
28.6
|
1.34
|
üretme
|
7.2
|
4.1
|
.57
|
5.2
|
3.6
|
.69
|
okuma-sıralama
|
38.4
|
172.6
|
4.49
|
27.4
|
126.6
|
4.62
|
üretme-sıralama
|
24.4
|
147.1
|
6.03
|
11.3
|
100.6
|
8.90
|
Programları kayan noktalı sayılar yerine tamsayılar kullanacak şekilde değiştirmek hız oranlarında bir değişiklik yapmadı. Yine de, bu değişikliğin C++ gibi yazılan programda C gibi yazılandan çok daha kolay olduğunu görmek güzeldi: 2 değişikliğe karşılık 12 değişiklik. Bu, program bakımı açısından çok çok iyi.
"üretme" denemelerinde görülen fark, bellek ayırma bedellerindeki farkları yansıtıyor. 'vector' ve 'push_back()'in hızının, bir dizi ile birlikte kullanılan 'malloc()' ve 'free()'nin hızıyla aynı olması beklenirdi; ama değil. Bu, boş işlevlere yapılan çağrılar, eniyileştirme sırasında atlanmadıkları için olmalı. Neyse ki bellek ayırmanın bedeli, bu bedeli doğuran girişin bedelinin yanında yok sayılacak kadar küçük.
Beklendiği gibi, 'sort()' 'qsort()'tan oldukça hızlı çıktı. Bunun ana nedeni, 'qsort()'un sıralamayı yaparken bir işlev çağırmasına karşın, 'sort()'un karşılaşıtırmayı kendi içinde yapmasıdır.
Verimlilik konularını gösterecek örnekler seçmek zor. Bir çalışma arkadaşım, sayı okuma ve sıralamanın doğal olmadığı yönünde bir yorum yaptı. Ona göre, okumayı ve sıralamayı dizgilerle yapmalıydım.Bunun üzerine aşağıdaki programı denedim:
#include
#include
#include
{
char* gkutuk = argv[2]; // giris kutugu adi
char* ckutuk = argv[3]; // çikis kutugu adi
fstream giris(gkutuk, ios::in);
string dizgi;
while(getline(giris, dizgi)) arabellek.push_back(dizgi); // giristen arabellege ekle
copy(arabellek.begin(), arabellek.end(), ostream_iterator(cikis, "n")); // cikisa kopyala
}
Dizgi okuma, sıralama, ve yazma
|
|||||
|
C++
|
C
|
C/C++ oranı
|
C (dizgi kopyalamadan)
|
Eniyileştirilmiş C/C++ oranı
|
500.000 öğe
|
8.4
|
9.5
|
1.13
|
8.3
|
.99
|
2.000.000 öğe
|
37.4
|
81.3
|
2.17
|
76.1
|
2.03
|
Nerede ne kadar süre geçtiğini anlamak için programı bir de 'sort()'u çıkartarak çalıştırdım.
Dizgi okuma ve yazma
|
|||||
|
C++
|
C
|
C/C++ oranı
|
C (dizgi kopyalamadan)
|
Eniyileştirilmiş C/C++ oranı
|
500.000 öğe
|
2.5
|
3.0
|
1.20
|
2.0
|
.80
|
2.000.000 öğe
|
9.8
|
12.6
|
1.29
|
8.9
|
.91
|
'string'in standart kitaplıkta bulunmasına rağmen aslında ek bir tür olduğuna dikkat edin. 'string' kullanarak yapabildiğimiz bu verimli ve güzel şeyleri başka ek türlerle de yapabiliriz.
Verimliliği neden programlama biçemi ve öğretimi bağlamında tartışıyorum? Öğrettiğimiz biçemler ve teknikler gerçek programlarda da uygulanabilmelidir. Büyük ölçekli ve belirli verimlilik ölçütleri olan sistemler de C++'ın hedeflediği sistemlerin arasındadır. Bundan dolayı, C++'ın insanları yalnızca basit programlarda kullanılabilecek biçemler ve teknikler kullanmaya yöneltecek şekilde öğretilmesini kabul edemiyorum. Bu, onları başarısızlığa ve öğrendiklerinden vazgeçmeye götürür. Yukarıdaki ölçümler, genel programlamaya ve somut türlere dayanarak basit ve tür güvenliği içeren C++ programları üretme biçeminin, geleneksel C biçemlerine göre daha verimli olduğunu gösteriyor. Nesneye dayalı programlama biçemleriyle de benzer sonuçlar elde edilmiştir.
Değişik standart kitaplık gerçeklemeleri arasında büyük hız farklarının olması oldukça önemli bir sorun. Standart kitaplığa veya yaygın olarak kullanılan başka kitaplıklara dayalı olarak program yapan bir programcı için, bir sistemde yüksek hızlar getiren programlama biçemlerinin, başka sistemlerde de hiç olmazsa kabul edilir düzeyde hızlar getirmesi önem taşır. C++ biçeminde yazılan programlarımın, C biçeminde yazılan eşdeğerlerinden bazı sistemlerde iki kere daha hızlı çalışmalarına rağmen, başka sistemlerde onların yarısı hızında çalıştıklarını görmek beni çok şaşırttı. Programcılar, sistemler arasında dört gibi yüksek bir hız katsayısını kabul etmek zorunda kalmamalıdırlar. Bu farklılığı getirecek görebildiğim hiçbir temel neden olmadığı için, kitaplık gerçekleyenlerin fazla uğraşa girmeden bu tutarlılığı sağlayabileceklerine inanıyorum. Standart C++'ın hem algılanan hem de gerçek hızını geliştirmenin en kolay yolu, belki de eniyileştirilmiş kitaplıklar kullanmaktır. Derleyici gerçekleyenler, başka derleyicilere karşı küçük hız kazançları sağlamak için yoğun bir çaba içindeler. Ben, standart kitaplık gerçeklemelerindeki gelişme kapsamının daha büyük olduğuna inanıyorum.
Yukarıdaki C++ çözümünün C çözümünden daha kolay oluşunun nedeni, standart C++ kitaplığını kullanmasıdır. Peki bu, karşılaştırmayı geçersiz veya haksız yapar mı? Sanmıyorum. C++'ın en önemli özelliklerinden birisi, düzenli ve verimli kitaplıkları desteklemesidir. Buradaki basit örneklerle gösterilen üstünlükler, düzenli ve verimli kitaplıkların olduğu veya yazılabileceği her uygulama alanında da geçerlidir. C++ topluluğunun karşısındaki güçlük, bu yararları sıradan programcıların kullanabilecekleri başka alanlara yaymaktır. Yani, başka birçok uygulama alanına yönelik düzenli ve verimli kitaplıklar tasarlamalı, gerçekleştirmeli, ve yaygınlaştırmalıyız.
"C++'ın önce hangi altkümesini öğrenmeliyim?" sorusunun geleneksel bir yanıtı, "C++'ın C altkümesini"dir. Benim kanımca, bu iyi bir seçim değil. Önce C'ye yönelmek, beraberinde alt düzey ayrıntılara fazla erkenden odaklanmayı getirir. Ayrıca programlama biçem ve tasarım konularını da öğrenciyi bir sürü teknik güçlükle yüz yüze bıraktığı için bulandırır. İkinci ve üçüncü bölümlerdeki örnekler bu noktayı açıklıyor. C++'ın kitaplık desteğinin, gösteriminin, ve tür denetiminin daha iyi olması, önce C'ye yönelmememiz gerektiği sonucunu doğurur. Ancak, benim önerimin "önce saf nesneye dayalı programlama" olmadığına da dikkat edin. Bence bu da başka bir uç nokta olur.
Bir dili öğrenme şekli, programlamaya yeni başlayanlara etkin programlama tekniklerini de öğretecek şekilde olmalıdır. C++'a yeni başlayan deneyimli programcılar için ise, etkin programlama tekniklerinin C++'ta nasıl kullanıldıklarına ve programcının ilk defa gördüğü tekniklerin anlatılmalarına odaklanmalıdır. Deneyimli programcıların karşılaştıkları en büyük engel, başka bir dilde etkin olarak kullandıklarını C++'ta dile getirmeye çalışmalarıdır. Hem yeni başlayanlar hem de deneyimliler için üzerinde durulacaklar, kavramlar ve teknikler olmalıdır. C++'ın desteklediği programlama tasarım ve tekniklerini anlamada, C++'ın sözdizimi ve anlamsal ayrıntıları ikinci derecede önemlidir.
Öğretmenin en iyi yolu, iyi seçilmiş somut örneklerden başlayıp daha genel ve daha soyut örneklere geçmektir. Bu hem çocukların öğrenme şekli, hem de bizim yeni düşünceleri kavrama şeklimizdir. Dilin olanakları her zaman için kullanıldıkları kapsamda sunulmalıdır. Yoksa programcının ilgisi, sistem üretmek yerine anlaşılması güç teknik ayrıntılara yönelir. Dilin teknik ayrıntılarıyla ilgilenmek eğlencelidir ama etkin bir öğretim biçimi değildir.
Öte yandan, programlamayı salt çözümleme ve tasarıma yardımcı olarak görmek de işe yaramaz. Kod üzerine yapılacak görüşmeleri üst düzey konuların sunulmasından sonraya bırakma hatasının bedeli, defalarca çok pahalıya ödenmiştir. Bu yaklaşım, insanları programlamadan uzaklaştırmaya ve üretim düzeyi niteliklerinde kod yazmanın getirdiği güçlükleri küçümsemeye yöneltmektedir.
"Önce tasarım" yaklaşımının tam karşıtı da, bir C++ gerçeklemesini alıp hemen kodlamaya geçmektir. Bir sorunla karşılaşıldığında tıklayarak yardım ekranlarında neler bulunacağına bakılır. Buradaki yaklaşımdaki sorun, özelliklerin ve olanakların, birbirlerinden ayrı olarak anlaşılmalarına dayalı olmasıdır. Genel kavramlar ve teknikler bu şekilde öğrenilemezler. Bu yaklaşımın getirdiği ek bir sorun, C++ sözdizimi ve kitaplıkları kullansalar bile, deneyimli programcıları daha önceden bildikleri bir dilde düşünmeye yönlendirmesidir. Sonuçta yeni başlayanların kodu, program örneklerinden kopyalanmış satırların bir sürü 'if-else' arasına serpiştirilmesinden oluşmaktadır. Yeni başlayanlar kopyalanan satırlardaki kodun amacını ve nasıl işe yaradığını çoğu zaman anlayamazlar. Kişi ne kadar akıllı olursa olsun durum değişmez. Bu "kurcalama yöntemi" aslında iyi bir öğretim ve iyi bir kitapla birlikte olduğunda çok yararlıdır ama tek başına kullanıldığında felakete davettir.
Ben özetle şöyle bir yöntem öneriyorum;
somuttan soyuta yönelmeli
dilin özelliklerini, destekledikleri programlama ve tasarım teknikleri kapsamında sunmalı
kodu, kuruldukları alt düzey ayrıntılara girmeden üst düzey kitaplıklara dayalı olarak sunmalı
gerçek programlara taşınamayacak tekniklerden kaçınmalı
ayrıntılara girmeden önce, benimsenmiş ve kullanışlı teknikler sunmalı; ve
dilin özelliklerinden çok, kavramlara ve tekniklere odaklanmalı.
Hayır, bu yöntemin yeni veya değişik olduğunu düşünmüyorum. Herkesin akla yakın bulacağını düşünüyorum. Ne yazık ki bu akla yakınlık; C'nin C++'tan önce öğrenilmesinin doğru olup olmadığı, nesneye dayalı programlamanın tam olarak anlaşılması için Smalltalk'un gerekip gerekmediği, programlamanın saf nesneye dayalı olarak mı (her ne demekse) öğretilmesinin iyi olduğu, ve kod yazmaya geçmeden önce yazılım geliştirme sürecinin iyice anlaşılmasının ne kadar önemli olduğu gibi tartışmalar arasında yok olup gitmektedir.
Neyse ki benim koyduğum ölçütler doğrultusunda biraz deneyimimiz var. Benim en sevdiğim yöntem; dilin değişkenler, bildiriler, döngüler gibi temel kavramlarını iyi bir kitaplık şeklinde öğretmektir. Öğrencilerin ilgilerini C dizgileri gibi karmaşıklıklar yerine programlamaya yönlendirmek için kitaplıklar gerekir. Ben standart C++ kitaplıklarını veya bir altkümelerini öneririm. Bu yöntem, Amerikan liselerinde 'bilgisayar bölümlerine hazırlama' derslerinde de kullanılmaktadır [Horwitz, 1999]. O yöntemin deneyimli programcılara yönelen daha geliştirilmiş bir şekli de başarıyla uygulanmıştır [Koenig, 1998].
Bu yöntemlerin bir zayıflığı, görsel programlamaya hemen girmemeleridir. Bunu karşılamanın bir yolu, arabirimi kolay olan görsel bir kitaplığı tanıtmaktır. Bu arabirim, öğrencilere C++ dersinin ikinci gününde verilebilecek kadar kolay olmalıdır. Ne yazık ki bu şartı sağlayan yaygın bir C++ görsel kitaplığı yok.
Baştaki bu kitaplıklara dayalı öğretimden sonra, öğrencilerin ilgileri doğrultusunda çok değişik konulara geçilebilir. Bir noktada, C++'ın düzensiz ve alt düzey bazı özelliklerine de değinmek gerekecektir. İşaretçi, tür dönüşümü, ve bellek ayırma gibi özellikleri anlatmanın bir yolu, temelleri öğretirken kullanılan sınıfların nasıl gerçekleştirildiklerini incelemektir. Örneğin 'string', 'vector', 'list' gibi sınıflar, C++'ın ilk derslerde gözardı edilen C altkümesini anlatmak için çok uygundur.
'vector' ve 'string' gibi değişken sayıda öğe barındıran sınıfları gerçeklerken, bellek yönetimi ve işaretçiler kullanılması gerekir. Sınıf gerçekleme tanıtılırken, önce gerçeklenmelerinde bu kavramların kullanılmalarına gerek olmayan 'Tarih', 'Nokta', ve 'SanalSayi' gibi sınıflar tanıtılabilir.
Ben soyut sınyfları ve sınıf hiyerarşilerini tanıtmayı genelde kapların ve kap gerçeklemenin anlatılmasından sonraya bırakıyorum ama bu konuda başka seçenekler de var. Konuların verildiği sıra, kullanılan kitaplıklara göre değişir. Örneğin sınıf hiyerarşilerine dayalı görsel kitaplıklar kullanan bir kurs, çok şekilliliği ve sınıf türetmeyi daha önce işlemelidir.
Son olarak, lütfen C++ dilini ve onun tasarım ve programlama tekniklerini anlatmanın birden fazla yolu olduğunu unutmayın. Öğrencilerin olduğu kadar öğretmenlerin ve ders kitapları yazarlarının da hedefleri ve çıkış noktaları farklıdır.
Eğitim, daha düzgün ve üst düzey programlama biçemlerine geçişte büyük rol oynar. Yersiz verimlilik kaygılarıyla alt düzey kitaplık olanaklarını kullanan yeni bir programcı kuşağının C++ kullanıcıları arasına girmesine gerek yoktur. Yeni başlayanlar kadar deneyimli olan programcılar da Standart C++'ı yeni ve üst düzey bir dil olarak öğrenmeliler ve alt düzey soyutlamalara ancak gerçekten gerek olduğunda inmelidirler. Standart C++'ı daha üstün ve sınıflar eklenmiş bir C gibi kullanmak, onun sunduğu olanakları harcamak anlamına gelir.
Değişkenlerin isimlerinin olmasını gerektiğini söyledik. Bir değişkeni kullanmadan önce onu tanımlamalıyız. Tanımlamayı değişkene uygun bir isim verme ve değişkenin hangi tipten olduğunu bildirmeyle yaparız.
Önce isterseniz değişleri C++ dili kuralların uygun bir biçimde nasıl isimlendireceğimizi görelim. Değişken isimlerini verirken C++'ın bir takım sıkı kurallarına uymamız gerekir. Bu kurallar:
Değişkenlerin isimleri alfabede bulunan karakterlerle başlamalı. Ama ilk harf hariç diğer karakterler sayı olabilir. C++ büyük ve küçük harf duyarlıdır. Yani Sayi, sayi ve SAYI hepsi ayrı değişken olarak algınalırlar. Değişken isimleri birden fazla kelime olduğu zaman; kelimelerin arasına boşluk konmaz. Bu tür değişkenleri ya kelimeleri birleştirerek veya kelimeler arasına _ (alt çizgi) karakteri koyararak isimlendiririz. Değişkenlerin isimleri !, ?, {, ] gibi karakterler içeremezler. C++'ın anahtar kelimelerini de değişken isimleri olarak kullanamayız. sayi, tamsayi1, toplam, Fark, KullaniciAdi, isim, _Adres, sinif_ortalaması, kurallar göre adlandırılmış değişkenlerdir. Diğer taraftan 1.sayi, tamsayi 1, fark!, 3.sinif_ortalamasi geçersiz değişken isimleridir. Böyle yanlış adlandırılmış değişkenleri içeren programlar derlenmez!
Anahtar kelimeler C++ dilinde bulunan komutların isimleridir. Bunları direk olarak değişken ismi olarak kullanamayız. Ayrıca alt çizgi ile başlayan değişken tanımlamadan kaçınmalıyız. Çünkü genelde C++ kütüphanelerini yazan programcılar değişkenlerini alt çizgi ile başlayan isimler verirler. Bu da isimler arasında çakışma yaratabilir. Değişkenleri isimlendirmeyi öğrendikten sonra sonra sıra C++ dilindeki temel veri türlerini öğrenmeye geldi.
Verileri bilgisayarda program çalışırken bellekte(RAM) depolanır. Bilgisayar belleği bitlerden oluşmuştur. Bir bit temel olarak 1 veya 0 değerini alır. Sekiz tane bit bir byte eder. Bilgisayarın hafızasında verilerin kapladıkları alanlar byte türünden ifade ederiz (bir çok sistemde bu böyledir). C++ verileri ihtiyacımıza göre değişik tiplerde tanımlarız kullanırız.
C++ dilinde hazır bulunan temel veri tipleri şunlardır:
Değişken
|
Boy
|
Açıklaması
|
Değer Aralığı
|
char
|
1
|
karakter veya 8 bit uzunluğunda tamsayı
|
signed: -128 ile 127 arasında
unsigned: 0 ile 255 |
short
|
2
|
16 bit uzunluğunda tamsayı
|
signed: -32768 ile +32767 arasında
unsigned: 0 ile 65535 arasında |
long
|
4
|
32 bit uzunluğunda tamsayı
|
signed: -2147483648 ile +2177483647 arasında
unsigned: 0 ile 65535 arasında |
int
|
|
Tamsayı tipidir. DOS'ta ve Win3.1'de 16 bit uzunluğunda ama Windows9x, WinNT, Win200 ve WinXP 32 bit.
|
short ve long türlerine bakınız.
|
float
|
4
|
Kesirli sayı.
|
3.4e +/- 38 (7 basamak)
|
double
|
8
|
Geniş ve fazla duyarlıklı kersirli sayı.
|
1.7e +/- 308 (15 basamak)
|
long double
|
10
|
double tipinin daha genişidir.
|
1.2e +/- 4932 (19 basamak)
|
bool
|
1
|
true(doğru) veya false(yanlış) değerini alır. Eski derleyiciler bu türü desteklemeyebilir. Yeni ANSI C++ standardında eklenmiştir.
|
doğru veya yanlış.
|
wchar_t
|
2
|
char tipinden geniş olur Unicode tipinde değişkenleri destekler.
|
geniş karakterler (unicode)
|
Değişkenleri isimlendirdik ve onların tiplerini öğrendik. Şimdi değişkenleri bildirmeyi ve onları kullanmayı öğrenelim. Genel olarak temel veri tiplerinden olan değişkenleri şu şekilde tanımlarız:
<veri_tipi> <deðiþken_isimi> ;
Yukarıdaki kurala uygun olarak aşağıda bununla ilgili örnekler vardır:
int sayi;
unsgined int a; char karakter;
float sayi_2;
bool dogru_yanlis;
unsigned long uzunTamsayi;
Yukarıdaki değişken tanımlamalarının hepsi kurallara uygundur. İstersek birden fazla değişkeni bir satırda tanımlama olanağımız vardır:
int sayi1, sayi2, sayi 3;
char karakter, baskabir_karakter;
Örnekte int tipinden üç değişkeni tek bir satırda tanımlamayı ve aynı şekilde char tipinden iki değişkeni tek bir satırda tanımlıyoruz. Burda dikkat edilmesi gereken nokta değişkenlerin arasına virgül koymamız gerektiğidir.
Değişkenlere değer atama işlemi için eşittir (=) operatörünü kullanırız. Mesela aşağıdaki kod parçasında önce x değişkenini sonra da y değişkenini tamsayı (int) tipinde bildirdik. Sonra programın herhangi bir yerinde x'in içeriğini 25 yaptık. Bunun hemen ardından y'nin değerini 14 yaptık. En son kısımda x'in değerini y'de depoladık.
int x;
int y;
......
x=25;
y=14;
....
y=x;
Değişkenlerin değerlerini ilk tanımladığımız anda da atayabiliriz. Aşağıda bununla ilgili örnekler verelim:
double t=3.25;
bool dogru_mu=false;
long int s1=12345, s2=-694312978425;
double t=3.25;
#include <iostream.h>
int main()
{
int sayi1;
int sayi2;
int toplam;
cout << "n Lutfen birinci tamsayiyi giriniz: ";
cin >> sayi1;
cout <<"n Lutfen ikinci tamsayiyi giriniz: ";
cin >> sayi2;
toplam = sayi1 + sayi2;
cout << "n " << sayi1 << " + " << sayi2 << " = " << toplam << endl;
return 0;
}
C++ İLE İLK PROGRAMIMIZ
// Bu bizim ilk C++ programimizdir.
// Ekrana "Merhaba Dunya" yazdiriyoruz:
# include <iostream.h>
{
cout << "Merhaba Dunya !";
}
Daha sonraki satır her C++ programında mutlaka bulunması gereken bir satırdır. Her C++ programında main() fonksiyonu olmak zorundadır; bu fonksiyonumuzun önünde ise o fonksiyonun döndürdüğü değişkenin veri tipi olmalıdır. Tabi ki C++ fonksiyonlar ve onların döndürdükleri değerler konusunu da ileride işleyeceğiz.
C++ fonksiyonlar ve kod blokları { } parantezleri arasında bulunmalıdır. mainde bir fonksiyon ise onun içindeki kodlar doğal olarak { } parantezleri arasındadır.
Programımızı derleyip, çalıştırdığımızda çıkan sonucun kodunun olduğu satır : cout << " Merhaba Dunya ! "; satırıdır. Bu satırda, iostream.h kitaplığında bulunan cout fonksiyonu sayesinde ekrana bir şeyler yazdırıyoruz. C++ dilinde her satır ifadenin sonuna ; koymak zorundayız. Bu duruma aykırı olan satırlar #include ile başlayanlar, ve fonksiyonlar başlangıç satırlarıdır. Bir kaç tane daha satır tipi de ; ile bitmez ama onları sonra göreceğiz. Aslında programımızı birkaç satırda da yazabilirdik. Sadece farklı ifadelerin sonuna ; koyarak. Mesela : # include <iostream.h> int main() { "Merhaba Dunya !"; return 0; } gibi..
return 0; ile programımızın (aynı zamanda main fonksiyonumuzun) çıkış noktasıdır. Eğer return ile 0 değeri döndürürsek programımızın güvenle çıktığını işletim sistemine bildirmiş oluruz.
C++ programlama dili, C'den aldığı çok hoş sözdimi (elegant syntax) ve alt seviyelerde ileri derecede esnek programlar yazmaya uygun olması ile gerçekten birçok bilgisayar programcısının favorisi olmuştur. Bütün bunların yanında büyük çapta yazılım geliştirirken gerçek dünyayı iyi bir şekilde modelleyen Nesne Yönemlimli Programlama özelliklerini de programcıya sunması onu gerçekten bir numara yapmıştır diyebiliriz.
Bu yazımızda C++ dilinin esnek programlamayı destekleyen bir özelliğinin, fonksiyon şablonlarının (function templates) üzerinde duracağız. Diyelimki bir programın içinde hem tamsayıları, hem de kesirli sayıları toplayan fonksiyonlara ihtiyacımız var. Bu durumda bir C programcısı muhtemelen tamsayi_topla(int sayi_1, int sayi_2) ve kesirlisayi_topla(double sayi_1,double sayi_2) şeklinde iki farklı fonksiyon yazardı. Aynı durumda bir C++ programcısı topla() isimli fonsiyonlara aşırı yükleme (method overloading) ile topla(int sayi1, int sayi2) ve topla(double sayi1,double sayi2) gibi aynı isimli iki tane fonksiyon yazar. İkinci durumda programcı yine 2 fonksiyon yazar ama burda sadece bu fonksiyonların ait olduğu sınıfın bir örneğini (instance) kullanan programcı rahat eder çünkü iki farklı fonksiyon ismi bilmek yerine sadece bir fonksiyon ismi bilir ve bunların aldıklar parametrelerin faklılıklarını bilir ve ona göre kullanır.
Yukarıdakilerin yanında aynı işi yapan ve farkları aldıkları ve/veya dönderdikleri değerlerin tipleri farklı olan n tane farklı fonksiyonu ayrı ayrı yazmak yerine bunlar için şablon fonksiyon şeklinde sadece bir metod yazıp bu fonksiyonları ihtiyacımıza göre çağırmak daha kolay olur. Hem böylelikle modern programlamadaki code-reuse (kodun tekrar kullanılması) ilkesini sonuna kadar kullanmış oluruz.
Şablon fonksiyon yazmak için öncelikle template< class Tip > böyle bir satırı fonksiyonumuzdan önce yazarız. Burdaki tip fonksiyomuzun çalışacağı veri tipidir ki bu temel veri tipi veya bir sınıf tipi de olabilir. Sonrai satır da ise Tip topla(Tip sayi1 , Tip sayi2 ) önce, fonksiyonun döndereceği veri tipini (bu void veya standart birşey de olabilir.), fonksiyon adını ve parametreleri yazarız. Parametrelerin bir veya birkaçı da temel bir veri tipi veya Tip'ten farklı bir sınıf tipi veya struct olablir. Sonra ise normal fonksiyon yazımı ile aynıdır.
Aşağıdaki programda topla() isimli şablon bir fonksiyonumuz var. Fonksiyon aldığı iki değişkeni toplayıp, toplamı geriye dönderiyor. Fonksiyonu denemek için iki tane tamsayı, kesirli sayı ve karakteri toplayıp sonuçlarını ekrana yazan kodları main() fonksiyonu içinde yazıyoruz. Tabi enson dummy değişkenini enson neden aldığmızı da tahmin ederseniz sanırım.
template< class Tip >
Tip topla(Tip sayi1 , Tip sayi2 )
{
return (sayi1+sayi2);
}
int main()
{
int tamSayi1=5, tamSayi2=10;
double kesirliSayi1=22.33, kesirliSayi2=55.26;
char karakter1='A', karakter2='B';
char dummy;
cout << "Iki tamsayý : "<< tamSayi1 << " ve " << tamSayi2;
cout << " toplayýnca = " << topla(tamSayi1, tamSayi2)<< endl <<endl;
cout << "Iki double : "<< kesirliSayi1 << " ve " << kesirliSayi2;
cout << " toplayýnca = " << topla(kesirliSayi1, kesirliSayi2) << endl<<endl;
cout << "Iki karakteri : "<< karakter1 << " ve " << karakter2;
cout << " toplayýnca = " << topla(karakter1, karakter2)<< endl <<endl;
cin >> dummy;
return 0;
}
Stdlib.h kütüphanesinde bulunan veri dönüşüm fonksiyonları şunlardır;
Dönen tip
|
Fonksiyon Adi
|
Argumanlar
|
double
|
atof
|
(const char*str)
|
int
|
atoi
|
(const char*str)
|
long
|
atol
|
(const char*str)
|
char*
|
ecvt
|
(double num, int n,int*dec,int*sign)
|
char*
|
fcvt
|
(double num, int n,int*dec,int*sign)
|
char*
|
gcvt
|
(double num, int n,char*buf)
|
char*
|
itoa
|
(int num,char*str,int radix)
|
char*
|
ltoa
|
(long num,char*str,int radix)
|
double
|
strtod
|
(const char*str ,char**endptr)
|
long
|
strtol
|
(const char*str ,char**endptr,int radix)
|
unsigned long
|
strtoul
|
(const char*str ,char**endptr,int radix)
|
char*
|
ultoa
|
(unsigned long num char*str ,int radix)
|
#include <iostream.h>
{
char *ConvertedFloattoString;
APositiveValue = 3.14159,
AScientificValue = 1.0239E4;
//Stdlib.h library' de belirtildiði sekilde
//fcvt(float olan sayiyi karakter katarina donusturen fonksiyon) fonksiyonunu kullaniyoruz.
//char* fcvt (double num, int n,int*dec,int*sign) sekline gore kullaniyoruz.
ConvertedFloattoString = fcvt(ANegativeValue,
precision,
&decimal,
&float_sign);
<<ANegativeValue
<<"n Float sayýyý stringe degistir:"
<<ConvertedFloattoString
<<"n Ondalýk deger : "
<<decimal
<<"n Float sayinin isaret(sign) degeri:"
<<((float_sign?"-":"+"));
precision,
&decimal,
&float_sign);
<<APositiveValue
<<"n Float sayýyý stringe degistir:"
<<ConvertedFloattoString
<<"n Ondalýk deger :"
<<decimal
<<"n Float sayinin isaret(sign) degeri:"
<<((float_sign?"-":"+"));
ConvertedFloattoString = fcvt(AScientificValue,
precision,
&decimal,
&float_sign);
cout <<"nn Float sayinin gercek degeri:"
<<AScientificValue
<<"n Float sayýyý stringe degistir:"
<<ConvertedFloattoString
<<"n Ondalýk deger : "
<<decimal
<<"n Float sayinin isaret(sign) degeri:"
<<((float_sign?"-":"+"));
}
Göstericiler C ve C++ dillerinin en zor konusu olarak ün salmışlardır. Ama konu üzerinde biraz çalışırsak ve göstericileri kullanırken dikkatli olursak gerçekten de programlarımızın hızını oldukça artıran araçlar olarak karşımıza çıkarlar.
Normalde bir değişken tanımladığımızda aslında sadece o değişkene hafızada yer ayırmış oluruz. Bu değişken ismiyle değişkenimizin hafızadaki yerine ulaşabiliriz. Değişken ismiyle değişkene ulaşmaya direkt referans (directly referance) denir. Göstericiler ise bir değişkenin hafızadaki yerini saklarlar. Bu şekilde göstericinin işaret ettiği değişkene de ulaşabiliriz. Buna dolaylı referans (indirectly reference) denir.
int *sayiPtr, sayi;
Yukarıda tamsayı tipinde bir gösterici bir de normal değişken tanımladık. Aynı satırda birden fazla gösterici tanımlarken herbirinin önüne ayrı ayrı * işareti koymamız gerekir. Aksi halde sadece ilk değişkenimiz gösterici olur. Birden fazla göstericiyi aynı satırda şöyle tanımlarız:
char *aPtr, *bPtri;
C++'da göstericiler için iki tane işleç(operator) kullanılır. Bunlardan birincisi: & adres işlecidir. Adres işleciyle gösterici ile aynı tipteki bir değişkenin adresine ulaşabiliriz.
int sayi=9;
int *sPtr;
sPtr=&sayi;
Yukarıdaki kodun ilk satırında bir tamsayı değişkeni tanımladık ve ona 9 değerini atadık. İkincisinde tamsayı tipinde bir adres tutmak için sPtr göstericimizi tanımladık. Son satırda ise sayi değikenimizin adresini sPtr değişkenimize yükledik. Artık sPtr, sayi değişkenini gösteriyor deriz.
cout << *sPtr << endl;
bu kod ekrana sayi değişkenimizin değeri olan 9'u yazdırır. Çünkü, sPtr göstericisi sayi değişkenini işaret ediyor ve *sPtr ifadesi ile sPtr göstericisinin işaret ettiği değişkene ulaşıyoruz.
Dolaylı referans işlecimiz ile ayrıca, göstericinin gösterdiği değişkenin değerini de değiştirebiliriz :
*sPtr=23;
cout<< *sPtr << endl;
cout<< sayi << endl;
Yukarıdaki kod parçasında önce sPtr göstericisinin gösterdiği değişkenin (yani "sayi" değişkenini) değerini 23 yaptık. Sonraki satırda sPtr göstericisinin gösterdiği değişkenin değerini ekrana yazdırdık. Son olarak sayi değişkenin değerini ekrana yazdırdık. Son iki satırda bulunan kodlar ekrana aynı değerleri yani 23 değeri basacaktır.
Hata yakalama (Exception Handling) başlıbaşına büyük bir konu olmasına rağmen Hata Yakalama ile ilgili temel bilgileri basit örneklerlerle anlatmaya çalışacağım. C dili bize çok az hata yakalama mekanizması sunar. Aşağıdaki kodu inceleyerek hata oluşma durumlarından neyi kastettiğimizi anlayabilirsiniz.
{
int *a, c;
FILE *dosya;
a = malloc(sizeof(int) * 10);
if (a == NULL)
return 1;
dosya = fopen("cpp.txt", "rb");
if (dosya == NULL) {
free(a);
return 2;
}
fread(a, sizeof(long), 10, b);
if (a[0] != 0x10) {
free(a);
fclose(dosya);
return 3;
}fclose(b);
c = a[1];
free(a);
return c;
}
Şimdi C++ 'da exception handling nasıl yapılır ona bakalım. C++ 'da hata yakalama mekanizması try,catch ve throw anahtar sözcükleriyle yapılır. try ve catch birer komut bloklarıdır. Hatanın ayıklanmasını istediğimiz bölgeyi try blokları içine almamız gerekir. Hata yakalandığında işletilecek kodlar ise catch blokları içinde olmalıdır. Peki try bloku ile catch bloku arasındaki iletişim nasıl sağlanacak, bunun cevabı ise throw anahtar sözcüğüdür. Hatanın oluşmasına sebeb olacak ifadeden sonra catch bloğuna hatanın türü ile ilgili bilgi göndeririz. Aşağıdaki programda throw ile atılan bir int bilgidir. catch bloğu ise bu bilgiyi alarak bir hata mesajı verir. Unutmayın throw ile atılan mesajdan sonra programımızı eğer exit() gibi bir fonksiyonla bitirmezsek catch bloğundan sonra programın akışı devam edecektir. Bu yüzden eğer programımızla ilgili hayati bir hata yakalarsak catch bloğu içinde exit() ile programı tamamen sonlandırmamız gerekir.
float a,b,c;
try{
cin >> b >> c ;
if (c==0) throw 1
a=b/c;
}
catch(int i)
{
cout << i<< " Hata olustu"
}
return 0;
}