Modern C++ (3): Uniform Initialization, override/final, default/delete, constexpr, etc.

Bir diğer Modern C++ yazım ile sizlerle birlikteyim 🙂 Önceki Modern C++ yazılarıma aşağıdaki bağlantılardan ulaşabilirsiniz.

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

Modern C++ (2): C++ 11 Yenilikleri

Bu yazımızda C++ 11 ile gelen yenilikleri incelemeye devam edeceğiz. C++11 ile gelen bütün yenilikler için yukarıdaki ikinci yazıma ulaşabilirsiniz. Bu yazımda diğer yenilikler kadar büyük değişiklikler olmasa da genel ilklendirme ve tanımlama yaklaşımlarını değiştirdiği için öğrenilmesinde fayda olduğunu düşündüğüm bir takım yapılardan bahsedeceğim. Yazımın sonuna doğru ise daha ufak çapta olan değişikliklerden bahsedecğim.

1. Initialization Lists/Uniform Initialization

C++ 11 ile gelen en önemli özelliklerden birisi de veri yapıları/tiplerinin ortak bir şekilde ilklendirilmesine yönelik sunulan “Uniform Initialization” olarak adlandırılan mekanizmadır. Peki C++ 03’de durum neydi? Şimdi hızlıca daha önceki duruma ve sıkıntılara bakalım, bu anlamda üç temel eksiklik bulunmaktaydı bunlar:

  • Sınıf üyesi dizilerin ilklendirilememesi,
  • Konteynerların (STL konteynerleri dahil) ilklendirilmesine ilişkin standart bir yolun olmaması,
  • Dinamik olarak oluşturulan POD veri yapılarının ilklendirilememesi.

Bunlarında yanında C++ 03 çok farklı ve ortak olmayan ilklendirme yöntemleri içeriyordu (temel tiplerin ilklendirilmesi, sınıf ve nesnelerin “data member” larının ilklendirilmesi, dizi ve karmaşık veri yapıların ilklendirilmesi). Şimdi hızlıca bunlara bakalım:

  • Basit veri yapılarının ilklendirilmesi için (=) operatörü kullanılmakta:

  • Constructor ları tanımlanmış sınıflar için constructor’lar aracılığı ile member initialization list, nesnelerin ilklendirilmesi için ise ” ( ) ” operatörü kullanılmaktaydı.

  • POD diziler ve karmaşık veri yapıları için ise ” { } ” kullanılmaktaydı.

Evet gelelim C++ 11 ile gelen yeni “Uniform Initialization”‘a. C++ 11 yeni ilklendirme yöntemi ile ile her bir veri tipinin (POD değişkeni, kullanıcı tanım constructor içeren sınıflar,  POD dizi, dinamik olarak oluşturulan dizi ve nihayetinde STL konteynerları) ilklendirilmesi için ortak bir yaklaşım sunmaktadır. Bu yaklaşım ” { } ” ilklendirmesi olarak ta ifade edilebiliyor ve ilklendirme için bu kullanılıyor.

Yukarıdaki örneklerde “={}” yerine “{}”  kullanımına dikkat edecek olursanız, ilki C++03 de kullanıllan yaklaşım. Bunun yanında “{}” boş parantez kullanımı da varsayılan ilklendirme anlamına gelmektedir (POD ve basit veri tipleri için 0 ile ilklendirme, sınıflar ve karmaşık veri yapıları için default constructor).

Bunlar ile birlikte kendi sınıflarınıza ve fonksiyonlarınıza da özellikle STL konteynerlerinin ilklendirilmesi benzer bir ilklendirme kazandırmak için C++ std::initializer_list template sınıfını sunuyor. Bunun sayesinde sınıflarınızı ilklendirme listeleri ile oluşturabilirsiniz. Aşağıda örnek bir sayı liste sınıfını bu şekilde nasıl ilklendirebileceğimize bakalım.

2. Override ve Final anahtar kelimeleri

Özellikle C++ “inheritance” mekanizması için bu anahtar kelime eklenmiştir. Bu anahtar kelime derleyiciye bunun eklendiği metodun bir üst sınıfta virtual olarak aynı şekilde tanımlanıp/tanımlanmadığını kontrol etmesini sağlar. C++ 03 ve önceki C++ larda aşağıdaki gibi durumlar kolay bir şekilde gözden kaçabilmekteydi.

Burada aslında amaç bir üst sınıfta tanımlanmış olan f metodunu tanımlamaktı ama metodun tanımı farklı olduğu için ikinci bir sanal metot tanımlandı. Aşağıda ise yeni kullanım var ve bu durumda derleyici hata vercektir.

“Inheritance” ile ilgili eklenen bir diğer anahtar kelime ise “final” dır. Bu anahtar kelime ile işaretlenen sınıf ve metotlar alt sınıflar tarafından tekrar tanımlanmasının önüne geçiyor. Bazı durumlar diğer sınıfların geliştirdiğimiz sınıfları inheritance üzerinden değiştirmeleri istemeyebiliriz (kripto, sisteml servisleri, vb.). Normal şartlarda aslında vector ve list benzeri sınıflar da bu tarz sınıflar içerisine dahil edilebilir. İşte bu amaçla aşağıdaki gibi sınıflarınız tanımlayıp bunların diğer sınıflar tarafından inheritance yolu ile değiştirilmesinin önüne geçebilirsiniz. Benzer şekilde metotlar da bu şekilde tanımlanarak, bu metotların diğer sınıflar tarafından geçersiz kılınmasının (override) önüne geçilebilir.

3. Default ve Delete anahtar kelimeleri

C++ 11 öncesinde herhangi bir sınıf içerisinde eğer bir “constructor” tanımlar iseniz, derleyici sizin için “default constructor” tanımlamaz ve aşağıdaki durumda derleme hatası alırsınız.

İşte “default” anahtar kelimesi ekleyerek derleyicinin bizim için “default constructor” oluşturmaya zorlayabiliriz.

“delete” anahtar kelimesi ile de metotların tanımlanması ve çağrılmasını engelleyebiliriz. Örneğin:

Yukarıdaki örneğin ikinci ve üçüncü ifadeleri engellemek isteyebiliriz. Aşağıdaki tanımlama ile buun önüne geçmiş oluyoruz.

4. constexpr

constexpr anahtar kelimesi derleyici tarafından derleme zamanında hesaplama yapılması için eklenmiş olan bir anahtar kelimedir. constexpr ile sadece değişkenler değil aynı zamanda metotlar ve dahi sınıf metotları da derleme zamanında hesaplanacak diye belirtilebilmektedir. Bu anlamda nesneler ile kullanımı const, metotlar ile kullanımı da aslında inline kullanımına benzerdir. Burada yine Uniform Initialization gibi tek bir yöntem kullanılması amacı da güdülmüştür.

constexpr ile tanımlanacak değişkenler aşağıdaki koşulları karşılamalıdır:

  • LiteralType olmalıdır,
  • Hemen oluşturulmalı ya da bir değer atanmalıdır,
  • Constructor’a ya da ilgili değişkene atılacak değerler sadece literal değerleri, constexpr değişkenler ve metotlar olabilir,
  • Nesne oluşturulmasında kullanılacak olan constructor, constexpr constructor gereksinimlerini karşılamalıdır. Explicit constructor olması durumunda constexpr olarak tanımlanmalıdır.

constexpr ile tanımlanacak olan metotlar aşağıdaki koşulları karşılamalıdır:

  • virtual olmamalıdır,
  • Dönüş değeri ve var ise parametreleri LiteralType olmalıdır,
  • Fonksiyon tanımı ya deleted/default olarak işaretlenmeli ya da sadece aşağıdakileri içermelidir,
    • null ifadeleri,
    • static_assert tanımlamaları,
    • class veya enum dışındaki typedef ya da alias tanımlamaları,
    • using tanımlamaları ve komutları,
    • tek bir return kullanımı
  • Fonksiyon tanımı aşağıdakileri içermemelidir
    • asm tanımlaması,
    • goto ifadeleri,
    • try-catch bloğu,
    • Literal olmayan, statik veya thread veri tanımlamalar.

constexpr ile tanımlanacak olan constructorlar aşağıdaki koşulları karşılamalıdır:

  • Parametreleri LiteralType olmalıdır,
  • Virtual base sınıflar olmamalıdır,
  • try-catch bloğu olmamalıdır,
  • Constructor tanımı ya deleted/default olarak işaretlenmeli ya da sadece aşağıdakileri içermelidir,
    • null ifadeleri,
    • static_assert tanımlamaları,
    • class veya enum dışındaki typedef ya da alias tanımlamaları,
    • using tanımlamaları ve komutları,
  • Bütün base sınıflar ve statik olmayan üyeler ilklendirilmelidir. Kullanılan bütün constructorlar da constexpr olmalıdır.

Daha detaylı koşullar için ConstExpr sayfasına başvurabilirsiniz.

5. Statik olmayan sınıf verilerinin ilklendirilmesi

C++ 11 ile artık statik olmayan sınıf üyeleri deklare edildikleri yerde ilklendirilebilecekler.  Bu sayede daha temiz (construtor küçülecek) ve okunabilir kodlar elde edebileceksiniz.

6. Sağ Template Ayraç Boşlukları

C++ 11 ile artık typedef tanımlamalarında garip boşluklar bırakmanıza gerek kalmıyor.

7. Inline Namespaces

C++ 11 ile eklenen bir diğer özellikte inline namespaces. Inline namespaces temelde size varsayılan olarak bir namespace içerisindeki tanımlamaların otomatik olarak bir üst namespace ile sunulmaısna olanak sağlıyor. Bu da bazı kabiliyetlerin özelleşmesi ve versiyonlama işini kolaylaştırıyor. Örneğin:

Özellikle framework ve kütüphane geliştiricilerin hangi namespacelerin otomatik olarak varsayılan işaretlenmesine olanak sağlayacak bir mekanizma. Ağer bu tarz bir yazılım geliştirmiyor iseniz çok kullanma ihtiyacı duymayabilirsiniz.

8. Delegating Constructors

C++ 11 ile birlikte artık ilklendirme listesi içerisinde aynı sınıfa ait diğer constructorları çağırabilirsiniz. Bu sayede yine kod azaltılarak okunabilirlik arttırılabilir. foo.foo; // == 0

9. static_assert

Bu yazımda sizlere bahsedeceğim son kabiliyet ise static_assert. static_assert ile derleme zamanında kontroller yapabilirsiniz.

 

Evet uzun bir yazının daha sonuna geldik. Bu yazı ile birlikte bir kaç konu dışında C++ 11 yeniliklerinin de bir çoğunun üzerinden geçmiş olduk, geriye “Smart Pointer”, “Lambda Expressions” ve “R-Value References” gibi baba konular kaldı. Bir de tabi STL’e gelen yenilikler var.

Muhtemelen bir sonraki yazımı da “Smart Pointer” ‘lara bakıyor olacağız.

Görüşmek dileğiyle…

Leave a Reply

Your email address will not be published. Required fields are marked *

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