C++ 11

Paylaş

C++ 11 veya Modern C++ olarak adlandırılan ve C++ diline eklenen yeni özelliklerle ilgili bilgiler örneklerle yer alıyor.

C++ 11 nedir?

C#, Java, Python gibi modern programlama dilleri dinamik bellek yönetimini çöp toplayıcı sayesinde otomatik olarak yapmasının yanında yazılım geliştiricilere çeşitli kolaylıklar getirdi.

Bunlar otomatik tür değişken tanımlama, daha kolay döngüler, daha kısa fonksiyon tanımı olarak sıralanabilir.

C++ dili uzun yıllar güncellenmemiş performans, bellek erişimi gibi nedenlerden dolayı kullanılmakta ve kullanımı giderek azalmaktaydı.

C++ 98 ve C++03 (2003) den sonra Modern C++ olarak adlandırılan C++ 11 (2011) ile modern programlama dillerinde yer alan otomatik tür değişkeni, lambda fonksiyonları gibi özelliği C++ diline kazandırdı.

C++ diline gelen özellikler örnekler üzerinden basit olarak anlatılacaktır.

NOT: Özellikerin daha farklı kullanımları olabilir. (var 🙂 )

1. auto

Değişken türünün otomatik olarak derleyici tarafından belirlenmesini sağlar.

#include<iostream>

using namespace std;

int main(){

    auto sira = 45;  // int
    auto kim = "Yusuf Sezer"; // char *
    auto maas = 12.34;  // double
    //auto bilgiler = new BirSinif();  // BirSinif referansı
    //auto sonuc = BirFonksiyon();  // BirFonksiyon dönüş türü

    cout << sira << " " << kim << " " << maas;

    return 0;
}

Sadece temel veri türü değil referans veri türü, hesaplama sonucu ve C++ STL içerisinde yer alan iterators içinde kullanılabilir.

2. nullptr

C++ dili C dilindeki tüm özellikleri aldığından C dilinde yer alan hataları da almıştır.

C dilinde kullanılan NULL pointer işlemlerinde sıklıkla kullanılır.

Ancak NULL anahtar kelimesinin değeri int türünden sıfırdır.

Ancak aşağıdaki gibi bir tanımlama belirsizliğe yol açmaktadır.

#include<iostream>

using namespace std;

void yazdir(int sayi){cout << "int" << endl; };
void yazdir(char* deger){cout << "char" << endl; };

int main(){

    yazdir(NULL);  //  hangisi çalışacak ?

    yazdir(nullptr);  //  yazdir(char*) çalışacak

    return 0;
}

Bu ve bunun gibi belirsizlikleri gidermek için NULL yerine nullptr anahtar kelimesi kullanmak faydalı olacaktır.

3. enum

C dilinde yer alan enum tür güvenliğine sahip değildi.

Tanımlanan değerlerin belirli bir kapsam alanı yoktu.

#include<iostream>

using namespace std;

int main(){
    // hatalı kullanım
    enum Gunler {PAZARTESI, SALI, CARSAMBA};
    int gun = CARSAMBA;
    cout << gun;

    // dogru kullanım
    enum class Gunler1 {PAZARTESI, SALI, CARSAMBA};
    Gunler1 secilengun = Gunler1::CARSAMBA;
    cout << (int) secilengun;

    return 0;
}

C++11 ile enum daha güvenli ve yeni özellikleri beraberinden getirmiştir.

4. Döngüler

C++11 range-based for loop ile diziler ve STL kütüphanesinde yer alan elemanları listelemek daha kolay hale geldi.

#include<iostream>

using namespace std;

int main(){
    
    int sayilar[5] = {5, 3, 8, 9, 11};
    int elemanSayisi = sizeof(sayilar) / sizeof(int);

    for(int i = 0; i < elemanSayisi; i++)
        cout << sayilar[i] << endl;

    for (auto mevcut : sayilar)
        cout << mevcut << endl;

    for (auto mevcut : {5, 3, 8, 9, 11})
        cout << mevcut << endl;

    return 0;
}

C++ STL taşıyıcıları range-based döngüsü ile uyumludur.

5. decltype

Bir değişken veya işlem sonucu ortaya çıkan değerin türüne göre değişken türü tanımlamak için kullanılır.

Anahtar kelimeyi auto anahtar kelimesinden ayıran en önemli özelliği referans türü ve const türü değerlere göre farklı işlem yapıyor olmasıdır.

Ayrıca bu ifade içerisine yazılan işlem sonucuna görede tür belirlemeye imkan verir.

#include<iostream>

using namespace std;

int main(){
    
    int sayi = 3;
    decltype(sayi) birdeger = 45;  // sayi değişkeninin türü int birdeger int türünden
    decltype(3*5) baskadeger = 55;  // işlem sonucu int baskadeger int türünden

    cout << sayi << endl;
    cout << birdeger << endl;
    cout << baskadeger << endl;

    return 0;
}

Anahtar kelime C++ ile Generic Programlama Template yapılarında faydalı olacaktır.

6. Lambda fonksiyonlar

C++ 11 ile gelen lambda ifadesi veya anonim fonksiyonlar daha kısa fonksiyon tanımlaması yapmaya imkan vermektedir.

#include<iostream>

using namespace std;

int main(){

    int sayi = 3;

    auto f = []{cout << "Merhaba lambda" << endl;};
    f();

    auto f1 = [sayi]{cout << sayi << endl;};
    f1();

    auto f2 = [=]{cout << sayi << endl;};
    f2();

    auto karesi = [](int deger){cout << deger * deger << endl;};
    karesi(5);

    return 0;
}

Lambda ifadeleri genellikle STL algoritmalarında kullanılan karşılaştırma fonksiyonlarında kod karmaşıklığını azalmak için kullanılmaktadır.

Lambda ifadesinde kullanılacak değişkenler (yakalama), parametre listesi, dönüş türü gibi geniş kullanımı vardır.

7. initializer_list

C++ 11 ile birlikte bir sınıfa değer vermek veya dizilere başlangıç değeri vermek için kullanılan süslü parentez ile değer verme işlemi için initalizer_list sınıfı geldi.

#include<iostream>
#include<vector>
#include<map>
#include<initializer_list>

using namespace std;

int main(){

    int sayilar[5] = {1, 3, 9, 88, 0};  // 

    vector<int> sayilar1;  // taşıyıcıya değer verme
    sayilar1.push_back(1);
    sayilar1.push_back(3);
    sayilar1.push_back(9);
    sayilar1.push_back(88);
    sayilar1.push_back(0);

    vector<int> sayilar2 = {1, 3, 9, 88, 0};  // taşıyıcıya initializer_list ile değer verme 
    vector<int> sayilar3 {1, 3, 9, 88, 0};  // taşıyıcıya initializer_list ile değer verme
    map<int,std::string> m { {0, "sifir"}, {1, "bir"}, {2, "iki"} };

    return 0;
}

Bu sınıf çalışma performansını arttırmak için çağrıldığında varsayılan olarak dizinin ilk ve son eleman referansını initalizer_list kurucusuna geçer.

8. Uniform Initialization

C++ ile süslü parentez kullanımı daha genişletildi. Böylece kurucu bir sınıfı süslü parentez ile oluşturma imkanı geldi.

#include<iostream>

using namespace std;

class Kisi {
public:
    string m_adi;
    string m_soyadi;
};

class Kisi1 {
public:
    Kisi1(const string& t_adi, const string& t_soyadi){};
};

int main(){
    // Aggregate Initialization
    Kisi yusuf = {"Yusuf", "Sezer"};

    // Uniform Initialization
    Kisi1 sefa = {"Sefa", "Sezer"};
    Kisi1 sezer {"Sezer", "Sezer"};

    return 0;
}

Uniform Initialization değerleri sırasıyla varsa initializer_list kurucusuna, eşleşen bir kurucuya veya aggregate initialization kurucusuna geçirir.

9. static_assert

Temel olarak geliştirilen yazılımdaki çalışma zamanındaki hataları bulmak veya değerleri test etmek için assert daha da geliştirildi.

#include<iostream>
#include<cassert>

using namespace std;

int main(){

    // run time
    assert(sizeof(int) == 4);

    // compile time
    static_assert(sizeof(int) == 3, "Bir hata olustu.");

    return 0;
}

assert işlemini static_assert ile artık derleme zamanında yapabilme imkanı getirilmiştir.

10. Delegating Constructor

C++ 11 ile sınıf kurucularının birbirini çağırmasına imkan veren delegating constructor özelliği gelmiştir.

#include<iostream>

using namespace std;

// C++ 11 öncesi
class Kisi {
private:
    string m_adi;
    string m_soyadi;
    void baslat(const string& t_adi, const string& t_soyadi){};
public:
    Kisi(const string& t_adi, const string& t_soyadi){ baslat(t_adi, t_soyadi); };
    Kisi(const string& t_adi){ baslat(t_adi, "Sezer"); };
    Kisi(){ baslat("Yusuf", "Sezer"); };
};

// C++ 11
class Kisi1 {
private:
    string m_adi;
    string m_soyadi;
public:
    Kisi1(const string& t_adi, const string& t_soyadi) : m_adi(t_adi), m_soyadi(t_soyadi) {};
    Kisi1(const string& t_adi) : Kisi1(t_adi, "Sezer") {};
    Kisi1(): Kisi1("Yusuf", "Sezer") {};
};

int main(){

    return 0;
}

Bu özellik sayesinde sınıfların yazılmasında daha az kod daha fazla işlev imkanı gelmiştir.

11. override

C++ OOP-NYP yapısı diğer NYP destekleyen dillerden farklı olarak çalışır.

Sınıf içerisindeki virtual anahtar kelimesi ile aşırı yüklenilmesi istenen bir sınıf tanımlayıp kalıtım alan sınıfta aşırı yüklenmesi sırasında imza sorunu ortaya çıkar.

Bu sorunu çözmek için C++ 11 ile birlikte override anahtar kelimesi eklenmiştir.

#include<iostream>

using namespace std;

class Insan {
    virtual void fonkA(int);
    virtual void fonkB() const;
};

class Kisi : public Insan {
    virtual void fonkA(float);  // yeni bir fonksiyon oluşturuldu.
    virtual void fonkB();  // yeni bir fonksiyon oluşturuldu.
};

class Kisi1 : public Insan {
    virtual void fonkA(float) override;  // Hata imzalar uyuşmuyor.
    virtual void fonkB() override;  // Hata imzalar uyuşmuyor.
};

int main(){

    return 0;
}

Bu anahtar kelime sayesinde derleyiciye metotların aşırı yüklendiğini imzaya dikkat etmesini açıkca belirtiyoruz.

12. final

OOP-NYP ile yazılım geliştirmede kalıtım kodun yeniden kullanılabilirliği ve genişletilebilirlik açısından önemli bir özelliktir.

Ancak bazen bazı sınıfların kalıtım alınması istenmeyebilir.

C++ 11 ile bir sınıfın kalıtım alınmasını veya metodun aşırı yüklenmesini engellemek için final anahtar kelimesi kullanılabilir.

#include<iostream>

using namespace std;

class Ogrenci final {};

class Kisi : public Ogrenci {};  // hata Ogrenci final

class Islemler {
    virtual void topla() final;
};

class Islem : public Islemler {
    virtual void topla() override {};  // hata Islemler::topla final
};

int main(){

    return 0;
}

C++ 11 ile birlikte gelen final anahtar kelimesi sınıf ve metot güvenliğini sağlar.

13. default

OOP-NYP destekleyen diller sınıf içerisinde tanımlanmayan boş kurucu, yıkıcı gibi metotlar derleyici tarafından otomatik olarak üretilir.

Ancak C++ için bu durum geçersizdir.

C++ 11 ile birlikte bu durumu belirtmek için default anahtar kelimesi eklenmiştir.

#include <iostream>

using namespace std;

class Kisi
{
private:
  string m_adi;

public:
  Kisi(const string &t_adi) : m_adi(t_adi){};
  Kisi() = default;
};

int main()
{
  Kisi yusuf;
  return 0;
}

Derleyiciye default anahtar kelimesi ile varsayılan kurucunun oluşturulması talimatı verilmiş oldu.

14. delete

Bir önceki default anahtar kelimesinin tersi işlem yapar.

Derleyiciler tarafından varsayılan olarak üretilen copy constructor, copy assignment operator, destructor ve default constroctor metotların üretimi iptal edilebilir.

Örneğin; Sınıf kullanılarak oluşturulan nesneler birbirine eşitlendiğinde derleyici tarafından otomatik olarak oluşturulan copy assignment operatörü çalıştırılır.

Bu operatörü devre dışı bırakmak için delete anahtar kelimesi kullanılır.

#include <iostream>

using namespace std;

class Kisi
{
private:
  string m_adi;

public:
  Kisi(const string &t_adi) : m_adi(t_adi){};
  Kisi &operator=(const Kisi &) = delete;
};

int main()
{

  Kisi yusuf("Yusuf");
  Kisi sefa("sefa");
  yusuf = sefa;

  return 0;
}

Nesneleri birbirine eşitlediğimizde delete anahtar kelimesi ile copy assignment operatörü devre dışı bırakıldığından çalışmayacaktır.

15. constexpr

Sabit tanımlamak için kullanılan const anahtar kelimesine benzer.

Ancak fark olarak fonksiyon ve hesaplama gibi sonucu constexpr olmayan ifadeleri alamıyor olmasıdır.

#include<iostream>

using namespace std;

int A() { return 3; }
constexpr int B() { return 3; }

int main(){
    
    const int sayi = A();
    //constexpr int sayi1 = A();
    constexpr int sayi1 = B();

    return 0;
}

Anahtar kelime const anahtar kelimesinin daha katı kurallısı denilebilir.

16. String Literals

C ve C++ içerisinde yer alan çeşitli karakterler özel anlam ifade eder.

Bu karakterlere escape character veya kaçış karakteri denir.

Bu karakterler program derlendiğinde tanımlandığı işi yapar. (\n yeni satıra geçirir gibi)

Ancak bazen bu durum istenmeyebilir bunun için C++ 11 ile birlikte yeni string sabitleri gelmiştir.

#include<iostream>

using namespace std;

int main(){

    string adi1 = "Yusuf\nSefa\nSezer";
    string adi2 = R"(Yusuf\nSefa\nSezer)"; // raw string
    string adi3 = u8"(Yusuf\nSefa\nSezer)"; // UTF-8 string
    const char16_t* adi4 = u"(Yusuf Sefa Sezer)"; // UTF-16 string
    const char32_t* adi5 = U"(Yusuf Sefa Sezer)"; // UTF-32 string

    cout << adi1 << endl;
    cout << adi2 << endl;

    return 0;
}

C++ R”()” ile yazılan ifade içerisinde yer alan kaçış karakterlerini işlemeyecektir.

17. tuple

C++ 11 ile birlikte gelen tuple sınıfı farklı türdeki verileri tutan bir değişken oluşturmaya imkan veriyor.

Bu sınıfın, class ve struct yapısından farkı bir tanımlama yapmadan oluşturulmasıdır.

Birden fazla farklı veri türünü class ve struct ile oluşturabiliriz ancak her farklı kullanım için ayrı class ve struct tanımının yapılması gerekir.

#include<iostream>
#include<tuple>

using namespace std;

tuple<int, string, double> sonuc(){
    tuple<int, string, double> deger(1, "Yusuf Sezer", 55.45);
    return deger;
}

int main(){
    tuple<int, string> s1 = make_tuple(44, "Sezer");

    cout << get<0>(s1) << " " << get<1>(s1) << endl;

    auto x = sonuc();

    cout << get<0>(x) << " " << get<1>(x) << " " << get<2>(x) << endl;

    return 0;
}

Oluşturulan değişken değeri get fonksiyon işlevi ile alınıyor.

Sınıf birden fazla türde değer döndürmek istenildiğinde kullanılabilir.

18. Kullanıcı tanımlı sabitler

Bir çok programlama dilinde değişkenlerin içeriği hakkında bilgi değişken adı veya varsa yorum satırından öğrenilir.

Örneğin; double boyu = 1.78; gibi bir ifade için boy özelliğini tutuyor diyebiliriz.

Ancak değişken adı farklı olduğunda değişken adıyla ilgili bilgi almak zorlaşacaktır.

C++ 11 ile birlikte gelen sabit tanımlama özelliği bu işlemi daha kolay hale getirmiştir.

#include <iostream>

using namespace std;

long double operator "" _m(long double meters) { return meters; }
long double operator "" _cm(long double cm) { return cm / 100.0; }
long double operator "" _mm(long double mm) { return mm / 1000.0; }

int main() {

    double boy = 178.0_cm;
    double boy1 = 1780.0_mm;

    cout << boy << endl;
    cout << boy1 << endl;

    return 0;
}

Kodlar incelendiğinde kullanıcı tanımlı sabitlerin kod okunabilirliğini arttırdığı görülebilir.

19. smart pointer

C++ ile yazılım geliştirirken en fazla karşılaşılan durum dinamik bellek yönetimi diyebiliriz.

Bellek sızıntısı temel olarak belleğin geri iade edilmemesi veya iade edilen belleğe tekrar erişmek diyebiliriz.

#include <iostream>

using namespace std;

class Kisi
{
private:
  string m_adi;
  string m_soyadi;

public:
  Kisi(const string &t_adi, const string &t_soyadi) : m_adi(t_adi), m_soyadi(t_soyadi){};
  void yazdir() { cout << m_adi << " " << m_soyadi << endl; }
  ~Kisi() { cout << m_adi << " " << m_soyadi << " silindi." << endl; }
};

int main()
{
  // memory leak - bellek silinmedi
  Kisi *yusuf = new Kisi("Yusuf", "Sezer");
  yusuf->yazdir();

  // Dangling - memory leak
  Kisi *sefa = new Kisi("Sefa", "Sezer");
  delete sefa;
  sefa->yazdir();

  return 0;
}

C++ 11 ile birlikte bellek yönetimini kolaylaştıran, smart pointer olarak adlandırılan ve memory dosyasında yer alan shared_ptr, unique_ptr, weak_ptr sınıfları gelmiştir.

Bu sınıflar temel olarak bellek yönetimini kolaylaştırmaktadır.

#include <iostream>
#include <memory>

using namespace std;

class Kisi
{
private:
  string m_adi;
  string m_soyadi;

public:
  Kisi(const string &t_adi, const string &t_soyadi) : m_adi(t_adi), m_soyadi(t_soyadi){};
  void yazdir() { cout << m_adi << " " << m_soyadi << endl; }
  ~Kisi() { cout << m_adi << " " << m_soyadi << " silindi." << endl; }
};

int main()
{
  // memory leak - bellek silinmedi
  /*Kisi *yusuf = new Kisi("Yusuf", "Sezer");
  yusuf->yazdir();

  // Dangling - memory leak
  Kisi *sefa = new Kisi("Sefa", "Sezer");
  delete sefa;
  sefa->yazdir();*/

  shared_ptr<Kisi> yusuf(new Kisi("Yusuf", "Sezer"));
  yusuf->yazdir();
  cout << yusuf.use_count() << endl;

  {
    shared_ptr<Kisi> sefa = yusuf;
    sefa->yazdir();
    cout << sefa.use_count() << endl;
  } // nesne bellekten silinmedi kullanım sayısı azaltıldı.

  return 0;
}

Smart pointer içerisinde yer alan shared_ptr temel olarak nesne sayısını tutar.

Aynı nesne birden fazla eklenebilir.

Nesne işini tamamladığında nesne sayısı 1 den fazlaysa nesne sayısını azaltır.

Nesne sayısı 0 olduğunda nesneyi bellekten siler.

Bu sınıfın aksine unique_ptr sınıfı nesne sayısını tutmaz.

Nesne sadece 1 defa eklenir.

Bu iki sınıfın kullanımı OOP-NYP içerisinde kullanılan composite olarak adlandırılan ve birbirine bağlı sınıfların kullanımında çeşitli bellek sızıntısı sorunları devam eder.

Birbirine bağlı sınıflar için weak_ptr kullanımı faydalı olacaktır.

20. diğer

C++ 11 sürümü ile birlikte Modern C++ olarak adlandırılmış ve bir çok özelik gelmiştir.

Bu özelliklerden göze çarpanları örneklerle açıklamaya çalıştım.

C++ 11 ile ayrıca move semantic, chrono, random, atomic, thread, mutex, regex gibi çeşitli sınıflar ve STL kütüphanesine yeni algoritmalar eklenmiştir.

Eklenen bu sınıflar genellikle C dilinde yer alan ancak çeşitli güvenlik sorunlarının önüne geçmek için güncellenmiştir.

Örnek olarak chrono sınıfı tarih ve saat işlemlerini daha esnek bir şekilde yapmaya imkan vermektedir.

Benzer şekilde Boost, pthread, OpenMPI gibi çeşitli kütüphaneler ile yapılan işlemler C++11 ile birlikte dilin standartına eklenmiştir.

C++ 11 ile birlikte artık sürekli güncellenen (14, 17, 20) bir dil olmuştur.

Bundan dolayı C++ referans sitelerinden yeniliklere bakmak faydalı olacaktır.

Çünkü C++ 11 ile gelen özellikler C++ 14 veya C++ 17 ile değişmiş olabilir.

Ayrıca C++ ile yazılım geliştirirken kullanılan derleyici seçiminede dikkat edilmelidir.

Bir derleyicide çalışan kod başka derleyicide çalışmayabilir veya daha farklı bir kullanımı olabilir.

Bundan dolayı ayrıca derleyici desteğinin de kontrol edilmesi faydalı olacaktır.

Hayırlı günler dilerim.


Bunlarda ilgini çekebilir


LinkedIn grubuna buradan katılın.