Modern C++ (1): nullptr, enum sınıfları, range-based döngüler, auto

Gerek akademik/iş hayatımda gerekse kişisel projelerimde ağılıklı olarak kullandığım programlama dili C++. Gerçi artık bir çok güçlü ve istediklerinizi hızlı bir şekilde gerçekleştirilmesine olanak sağlayacak programlama dilleri olsa da (C#, Ruby, Kotlin, Python, Java, vs), ben C++ ı daha çok tercih ediyorum. İlk başlarda ilgilendiğim konularda (2B/3B bilgisayar grafikleri, oyun geliştirme, simülasyon sistemleri, gömülü sistemler) yaygın olarak kullanılması sebebi ile başladı daha sonra da duygusal bir hal aldı. Kısacası seviyorum uleynn 🙂

Bu ve sonraki “Modern C++” etiketli yazılarımda bence her C++ geliştiricisinin bilmesi gereken bir takım yeni özelliklerden bahsedeceğim. Tabi modern derken ne kast ettiğimi merak ediyor olabilirsiniz. Aslında C++ oldukça eski bir programlama dili [Wikipedia]. Tasarımcısı Bjarne Stroustup amca dili ilk olarak 80 lerin başında yayınladı. O zamanlar doktora çalışmaları ile bu işe girişmiş ve daha sonraları C++ ı şekillendirmiştir, halen de aktif olarak çalışmalar da yer alıyor.

İlk başlarda o zamanlar kullanılmakta olan C programlama dilini baz alan ve bu dile sınıf mekanizmalarını getiren (C with Classes olarak ta bilinen) bir dil olarak 1979 da çıktı. 90 larda gerçekleştirilen standartlaştırma ve C++ komitesinin kurulması sonrasında 2003 de, şu anda da oldukça yaygın olarak kullanılan 2003 C++03 (ISO/IEC 14882:2003) oluşturuldu. Sonrasında ise 2011 lere kadar çok ciddi bir güncelleme olmadı ama 2011 den itibaren gayretli çalışmalar sonrasında dil bir metamorfoz geçirerek Modern C++ dediğimiz hali almaya başladı ve sonrasında 2011 (C++0x), 2014 (C++1y) ve nihayet daha dumanı burnunda olan (temmuz 2017 de yayınlandı) 2017 (C++1z) ortaya çıktı. Bunlar ile C++ gerçekten çok güçlü ve bir yönden de bambaşka bir dil haline geldi. İşte modern C++ dediğimde aslında bu son üç yayın ile birlikte gelen kabiliyetlerden ağırlıklı olarak bahsediyor olacağım. Aşağıda aktif olan C++ standartlarını görebilirsiniz:

YılC++ Standardıİsim
1998ISO/IEC 14882:1998C++98
2003ISO/IEC 14882:2003C++03
2011ISO/IEC 14882:2011C++11 / C++0x
2014ISO/IEC 14882:2014C++14 / C++1y
2017ISO/IEC 14882:2017C++17 / C++1z
2020to be determinedC++20[15]

Aşağıda anlatacağımız bütün kabiliyetler C++11 den beri sunulmakta ve muhtemelen piyasadaki aklı başında çoğu C++ derleyicisi ile sunulduğunu düşünüyorum. Ama tabi sizin bunları denemek için bir derleyicisi kurmanıza gerek yok 🙂

https://www.jdoodle.com/online-compiler-c++14 i her zaman kullanabilirsiniz (buna da bir yazı ayırmalıyım 🙂

1. nullptr

Eğer siz de daha önce C programlama dili kullandı iseniz veyahut C++ pointer aritmetiği ile uğraştı iseniz. NULL pointer ifade etme isteği ve ihtiyacını hissetmişsinizdir. Yani herhangi bir değer içermeme durumu. C ve C++ da bu anlamda her ne kadar 0 kullanılsa da (NULL, Null olarak değişkenlere atansa da) bu aslında sayısal bir değer ve bir pointer tipi değil. Bu çoğu kullanım için sıkıntı yaratmayabilir, fakat problem de olabileceği durumlar olabilmekte 🙂 Örneğin:

İlk bakışta ikinci tanımlanan metot çağrılacak gibi görünse de, aslında ilk metot çağrılır. Peki neden? Aslında cevap basit tanımlanan NULL bir tam sayı tipi ve bunu parametre olarak ilk metot.

İlk bakışta ne olacak ki bundan denilebilir veya çok sık karşılaşılacak bir durum olmadığı aşikar fakat bu durum yüzlerce satır kod arasında oluştuğu durumda tespit etmek sıkıntı olabilir (hele ki altında yatan mekanizma bilinmediğinde).

İşte bu tarz durumları ortadan kaldırmak ve pointer da değer atanmamış durumu daha net ifade etmek adına C++ 0x ile birlikte nullptr anahtar kelimesi eklenmiştir. nullptr bütün pointer tanımlamalarında kullanılabilir ve özel bir tip (std::nullptr_t) olarak tanımlanmıştır. Tip bilgisine;

ile elde edilebilir. 0 halen valid bir null pointer değeri olarak dil tarafından kabul edilse de bundan sonra NULL, 0 kullanımına ihtiyaç hissettiğiniz her yerde nullptr kullanınız (ve de kullanmalısınız).

Aşağıda bir takım örnek kullanımları da ekledim:

2. Enum sınıfları (Type-safe enumerations)

Kısaca enum sınıfları daha önce C++ da kullanılan C-style enum’lerde karşılaşılan aşağıdaki sıkıntıları ortadan kaldırmaktadır:

  1. Implicit conversion,
  2. Tanımlanma alanı kirliliği
  3. “forward declaration” yapamama sıkıntısı
  4. Enum değerlerine tip/boyut atayamama,

Şimdi gelelim C++0x öncesi enumlara (bu arada eski tip enumler desteklenmeye devam ediyor ama artık kullanmayalım lütfen 🙂 Mevcut C++ enum değerleri aslında alt tarafta bir tam sayı ile ifade edilmektedir.  Bu tanımlanan değerler diğer tam sayı veya enum değerleri ile karşılaştırılabilmektedirler. Kodlama açısından bu bir sıkıntı oluşturmamaktadır, sonuçta ikisi de alt tarafta bir sayı. Fakat anlamsal olarak sakıncalı ve alakasız durumlar ortaya çıkabilmektedir (meyve tipi ile 5 i karşılaştırma, veya elma ile sarı renk enum değerlerini karşılaştırma gibi eğer bunların sayısal değerleri aynı ise bunlar aynıdır sonucu alınabilir). Bu sıkıntı aslında başta ifade ettiğimiz 1 no lu sıkıntıları adreslemektedir.

2. duruma gelince; eski tip enumlar ile tanımlanan değerlerin tanımlama alanları sınırlandırılmamıştır.  Bu problem Name Collision olarak ta ifade edilmiştir. Yani aynı isme ait farklı enum tipleri de olsa bunları aynı tanımlama alanında tanımlayamazsınız. Örnek ile daha açık olacak sanırım buyurun 🙂

Yeni gelen enum sınıfları ile birlikte yukarıdaki tanımlamalar  artık aşağıdaki gibi tanımlanabilir.

Şu an mevcut bir çok derleyici :: ile olan kullanımı desteklese de bu standart değildir. Örnekte ayrıca forward declaration’a da örnek verilmiştir. Bu neden önemli. Bu sayede özellikle sık değişen enumların olması durumunda bütün kodun baştan derlenmesi yerine sadece bunların kullanıldığı cpp kodlarının derlenmesi yeterli olacaktır.

Son olarak, eski enum kullanımlarına ilişkin bir diğer sıkıntı tanımlanacak olan enum değerinin boyutunu belirleyememenizdir. Kaç byte olarak tutulacağı ile ilgili dikte edilen bir standart olmasa da genelde el ile girilen en büyük değeri saklayabilecek büyüklükte olmaktadır. Artık bu belirlenebilmekte ve signed ya da unsigned olan tam sayı tipi bu amaçla kullanılabilir. Varsayılan değer int tir.

3. Range-based döngüler

Standart for döngüleri için “syntactic sugar” olarak sunulan range-based döngüler artık C++ için de kullanılabilecek (c# ve Java gibi dillerde zaten bulunmaktaydı).

Range-based döngüler C tipi diziler, initializer list’ler (bunu bir sonraki yazıma bırakıyorum) ve STL de tanımlanan iterator ve begin() / end() metotları tanımlanan sınıflar için kullanılabilir.

4. auto

Tanımlanan değişken tiplerinin sunulan ilklendirme veri tiplerine göre derleyici tarafından otomatik olarak atanması kabiliyeti de artık C++ a geldi. Çoğu betik dil kullananlar bunu sevecektir 🙂

C++ 0x den önce auto veri tipinin tanımlanma alanı ömrünü belirtmek için varsayılan olarak bütün değişkenlere ekleniyordu. Artık auto veri tipinin yerine kullanılacak ve derleyiciye kardeşim sana zahmet bu tipi otomatik olarak algılar mısın diye rica da bulunacak. Aşağıda çeşitli örnek kullanımlar verilmiştir:

Açıkçası yukarıdaki tanımlamalar için çok ta büyük bir faydası yok gibi görünse de asıl kullanım kolaylığı karmaşık veri tipi tanımlamalarında ortaya çıkmaktadır örneğin:

Ayrıca C++1y (14) ile birlikte artık fonksiyonların dönüş değerlerinde de auto kullanılabiliyoruz 🙂

Performans anlamında herhangi bir ek maliyeti yoktur.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

This site uses Akismet to reduce spam. Learn how your comment data is processed.