C++’taki pek çok temel bilgi aslında C dilindeki bilgiler ile hemen hemen aynıdır. Ancak ufak da olsa bazı farklılıklar mevcuttur. Burada size C++ dilini anlatırken C dilinde anlattıklarımızı tekrarlamamaya özen göstereceğim. C diline giriş yaparken bildiğiniz gibi ekrana bir Merhaba Dunya! yazısı yazdırmıştık. Bunun için printf ve puts gibi fonksiyonlardan faydalanmıştık. C++’ta bunu yine yapabiliriz. Ancak önerilen yöntem bu değildir.
C++ dilinde C dilinin üzerine pek çok farklı özellik ve operatör eklenmiştir. Örneğin C dili girdi/çıktı ile alakalı dile özel herhangi bir özellik sağlamaz. Yani bunlarla ilgili bir operatör veya anahtar kelime yoktur. Bunun yerine girdi/çıktı işlemlerini fonksiyonlar ile sağlamaktadır. Fakat C++’ta girdi/çıktı işlemleri dilde önemli bir yer tutar. Bu işlemlere özel operatörler ve dil kısıtlamaları bulunmaktadır. Örneğin bu kısıtlamalardan biri, C++’ta girdi/çıktı işlemlerinde tür belirtmenin zorunluğu olduğudur. Böylelikle tür bakımında güvenli (type safe) işlemler gerçekleştirebilir ve birçok hatadan kurtulabilirsiniz.
C++’taki bir diğer farklılık ise, girdi/çıktı iletişiminde Stream (Akış) adı verilen yapıların kullanmasıdır. Bu yapılar girdi ve çıktı nesneleri arasında akan byte’lar serisinden meydana gelirler. Bunu tıpkı su borularına benzetebiliriz. Su borularının görevi de aslında suyu kaynak birimden hedef birime taşımaktır. İşte Stream’lerin kullanım amacı da verileri kaynak giriş aygıtından (klavye, dosya, ağdaki bir bilgisayar vb.) hedef çıkış aygıtına (konsol ekranı, dosya, ağdaki bir bilgisayar vb.) taşımaktır.
Elbette bu Stream yapıları ile beraber dile 2 yeni operatör eklenmiştir. “<<” ile belirtilen Stream Insertion (Akışa Ekleme) operatörü ile veriler bu Stream yapısına eklenirken, “>>” ile belirtilen Stream Extraction (Akıştan Çıkarma) operatörü ile veriler bu Stream yapısından çıkarılabilir (alınabilir). Bu operatörlerin bir tarafında okuma veya yazma yapılacak olan ifadeler yer alırken, diğer tarafında giriş veya çıkış nesnesi yer almaktadır. “<<” operatörü için örnek kullanıma bakalım:
float a = 6.7; cout << 5; cout << a; cout << "Merhaba";
Gördüğünüz gibi bir tarafta standart çıktıyı belirten cout yer alırken (bunu birazdan açıklayacağım), diğer tarafta bu çıktıya Stream yoluyla gönderilen değerler yer almaktadır. Bu kod parçaları ile ekrana çeşitli türlerde değerler basılmıştır. Yukarıda ilk olarak int türünden 5 sabiti, daha sonra float türünden a değişkeninin değeri, en son da String sabiti olan "Merhaba" ekrana basılacaktır.
Şimdi C++’ta cout, cin ve cerr gibi kelimelerin neyi ifade ettiklerini açıklayalım. Bunlar C++’ta sırasıyla standart çıktı, girdi ve hata Stream’lerini belirten nesnelerdir. Bu nesneleri kullanarak ekrana herhangi bir çıktı veya hata çıktısı verebilir, ayrıca kullanıcıdan girdi alabiliriz. Bu nesneler sayesinde printf ve scanf gibi güvenli olmayan fonksiyonlardan kurtulup C++’ın daha kontrollü girdi/çıktı dünyasına adım atarız. Bu nesnelerin ne anlam ifade ettiklerini şöyle açıklayabiliriz:
- cout: “<iostream>” başlık dosyasında yer alan bu nesne ostream türündedir ve standart çıktı Stream’ini belirtir. Bunun anlamı bu nesne sistemde tanımlı varsayılan çıktı aygıtını belirtir. Bu nesneye Stream operatörleri ile gönderilen değerler standart çıktı aygıtına basılır. Genellikle bu aygıt konsol ekranınız olur.
- cin: “<iostream>” başlık dosyasında yer alan bu nesne istream türündedir ve starndart girdi Stream’ini belirtir. Bunun anlamı bu nesnenin sistemde tanımlı olan standart girdi aygıtını (genellikle klavye) belirttiğidir. Kullanıcıdan girdi almak için kullanılır.
- cerr: “<iostream>” başlık dosyasında yer alan bu nesne ostream türündedir ve standart hata Stream’ini belirtir. Bunun anlamı bu nesne sistemde tanımlı varsayılan hata çıktısı aygıtını belirtir. Bu aygıt da tıpkı cout nesnesinde olduğu gibi genellikle konsol ekranıdır.
Gördüğünüz gibi bu nesneler ile kullanıcılar ile çok rahat bir biçimde ve daha güvenli olarak etkileşime girebiliyoruz. Biraz önce cout nesnesi ve “<<” operatörü ile ilgili örneği gösterdim. Şimdi de cin nesnesi ve “>>” operatörü işe ilgili bir örnek vereyim:
int a; cin >> a; cout << a;
Gördüğünüz gibi burada int türünden bir a değişkeni tanımlanıp kullanıcıdan bu değişkene bir değer ataması istenmiştir. Bu da cin nesnesi ve “>>” operatörü ile yapılmıştır. Bu operatörün sol tarafı giriş aygıtını belirten nesneyi alırken, sağ tarafı kullanıcıdan alınan değeri tutacak olan değişkeni almaktadır. Daha sonra da bu değeri cout ve “<<” kullanarak ekrana basıyoruz. “<<” ve “>>” operatörlerinin genel kullanım biçimleri şöyledir:
<“ostream” Nesnesi> << <Değer>
<“istream” Nesnesi> >> <Değişken>
ostream ve istream nesneleri, C++’ta bu sınıf türünden oluşturulmuş nesneleri belirtmektedir. Sınıfları görürken bunu daha iyi anlayacaksınız. Şimdi bazı temellerden bahsettikten sonra ilk C++ programımızı yazıp inceleyebiliriz:
// Code 1.3.1: Code1_3_1.cpp
// Merhaba Dünya Örneği
#include <iostream>
int main()
{
std::cout << "Merhaba Dunya!\n";
return 0;
}
Burada C dilinde yazdığımız örnekten farklı olan noktalara bakalım. Öncelikle ilk gözümüze çarpan farklılık 4. satırdaki include önişlemci komutu ile bir başlık dosyasını programımıza ekleme kısmıdır. Burada başlık dosyasına “.h” uzantısını getirmeden, sadece isim yazarak erişmekteyiz. C++’ın standart kütüphanesinde yer alan başlık dosyaları genellikle bu şekilde programımıza eklenir. Yani yine “.h” başlık dosyası kullanılabilir, ancak bu tip uzantısız yazım, standart C++ kütüphanelerinin diğerleri ile karıştırılmaması için önerilmiştir.
8. satıra baktığımızda ise (zaten kayda değer başka bir satır yok), cout nesnesinin başında std:: ekini görmekteyiz. İşte burada C++ diline eklenen Namespace (İsim Uzayı) kavramından kısaca bahsetmekte fayda var. Namespace’ler aslında ileride göreceğimiz sınıfları belli bir isim altında paketlemek için dile eklenmiş yapılardır. Buradaki std kelimesi de tanımlanmış bir Namespace’in adıdır ve standart kütüphane nesnelerini çağırırken kullanılır. Buradaki “::” operatörü (ayrıntısını daha sonra göreceğiz) bu Namespace’de yer alan bir nesneyi çağırmak için kullanılmaktadır. Biz yukarıdaki kodu şu şekilde de yazabilirdik:
#include <iostream>
using namespace std;
int main()
{
cout << "Merhaba Dunya!\n";
return 0;
}
Bu şekildeki yazımda std Namespace’ini programın başında kullanmak üzere tanımlamış oluruz. Böylece bu Namespace’e ait olan nesneleri çağırırken, başına std:: gibi bir ön ek getirmek zorunda kalmayız. Pratikte iki kullanım da tercih edilmektedir. Eğer Namespace’ler kafanızı karıştırıyorsa ikinci kullanımı tercih edebilirsiniz. Fakat çok fazla Namespace’in bulunduğu bir projede, hangi nesnenin hangi Namespace’den olduğunu belirtmek daha iyi olacaktır. Bu durumda ikinci kullanımı tercih edebilirsiniz.
Son olarak iki sayıyı toplayan bir program yazalım. Bu programda kullanıcıdan iki sayı alacak ve toplamı ekrana basacağız. Hemen koda bakalım:
// Code 1.3.2: Code1_3_2.cpp
// İki sayının toplamını bulma
#include <iostream>
using namespace std;
int main()
{
int sayi1, sayi2, toplam;
cout << "Lutfen bir sayi girin: ";
cin >> sayi1;
cout << "Lutfen bir baska sayi girin: ";
cin >> sayi2;
toplam = sayi1 + sayi2;
cout << "Girilen sayilarin toplami: " << toplam << endl;
cout << "Program basariyla sonlandi!";
return 0;
}
Lutfen bir baska sayi girin: 6
Girilen sayilarin toplami: 11
Program basariyla sonlandi!
Burada 20. satıra kadar çok farklı bir şey yapmıyoruz; kullanıcıdan iki sayı alıp onları sırasıyla sayi1 ve sayi2 değişkenlerinde depolayıp toplamı toplam değişkenine atıyoruz. 20. satırda ise “<<” operatörünün ard arda kullanımını görüyoruz. Bu operatör ard arda kullanıldığında değerleri soldan sağa doğru Stream’e basar. En sonda kullandığımız endl ise ostream sınıfı türünden olan ön tanımlı bir nesnedir. Bu nesne ile bir satır aşağı inme karakteri olan “\n” ile aynı şeyi yapabilirsiniz. Bu nesnenin o karakterden tek farkı ise bir alt satıra indikten sonra Stream’i boşlatmasıdır (flush).
Peki bir Stream’in boşaltılması ne anlama gelir? Bildiğiniz gibi Stream’ler veriler ve aygıtlar arasında bir boru görevi görmekteydi. Borular fiziksel olarak da belli hacimde suyu bir süre üzerlerinde tutabilen araçlardır. İşte Stream’den çıktı cihazına aktarma yapılmadan önce veriler bir süre burada tutulur. Özellikle yavaş erişimli cihazlarda yazma gerçekleştirirken (dosyalar gibi) her bir karakteri yazdıktan sonra dosyaya tekrar tekrar erişmek istemeyiz. Bu nedenle yazacaklarımızı tampon (buffer) adı verilen bu borularda biriktirir ve en son yazmak isteriz. Bir tamponu boşaltmak ise, verileri cihaza gönderip tamponu tamamen boş hale getirmek demektir. İşte burada endl nesnesinin yaptığı ikinci iş budur.
Bu yazıda C++ ile programlamaya kısa bir giriş yaptık. C dilinde farklı olarak C++’ın girdi ve çıktılarda nasıl davranış sergilediğini ve hangi mekanizmalara sahip olduğunu açıkladık. Ayrıca temel olarak C++ programları yazarken önümüze çıkacak bazı farklılıkların açıklamalarını yapmaya çalıştık. Bir sonraki yazıda nesne yönelimli programlama yaklaşımı ile ilgili güzel bilgiler verip bu bölümü kapatmayı düşünüyorum.



