C & C++Programlama

C++ Bazı İpuçları ve Püf Noktaları

Halen popüler ama eski nesil diller nelerdir denilince ve özellikle karizmatik olanları saymak gerekirse önce C, sonrasında C++ programlama dilleri akla geliyor. Düşük seviye dillerden olan C++, atası olan C dili üzerine aldığı gelişmelerden oluşturulmuştur ve özellikle sistem ve oyun programlamalarda çok tercih edilen bir dildir.

C++ dili aslında yeni nesil dillere göre kıyaslandığında biraz daha ağır kaçmakta, öyle bir kaç video ve ders izlemekle öğrenmek mümkün olmamaktadır. Ancak öğrenme aşamasında veya ileri durumlarda işinize yarayabilecek bazı C++ ipuçları paylaşmak istiyoruz.

try-catch fonksiyonlarla kullanımı

Bilindiği gibi bir çok dilde standart olarak hata yakalama ve bilgilendirme olarak kullanılan try-catch ifadesi fonksiyon seviyesinde de kullanılabilmektedir.

Genel yapı şu şekildedir

bool foobar()
try
{
   std::cout << "foobar" << std::endl;
   throw std::exception("foobar hatalı");
}
catch (std::exception const & e)
{
   std::cout << e.what() << std::endl;
   return false;
}

int main()
{
   auto f = foobar();
   std::cout << std::boolalpha << f << std::endl;
}

Bunu bir örnekle yapmak istersek;

bool foobar()
try
{
   // Birşeyler yap ve işlem başarılı ise sonucu "true" olarak döndür
   return true;
}
catch (std::exception const & e)
{
   std::cout << e.what() << std::endl;
   return false;
}

Bunu fonksiyon içerisinde aşağıdaki şekilde de yazmak mümkündür;

bool foobar()
{
   try
   {
      // Birşeyler yap ve işlem başarılı ise sonucu "true" olarak döndür
      return true;
   }
   catch (std::exception const & e)
   {
      std::cout << e.what() << std::endl;
      return false;
   }
}

Fonksiyon içinde try-catch kullanımı mantıklı olmakta ve özellikle constructor‘lar için dizayn edilmiştir. Ancak constructor ile olası bir problem görülebilmekte zaman zaman. Şöyle ki; Constructor tam constructor olmayan bir objeyi (object) dışa çıkarırsa (throw) (ki constructor verilen işlemi başarıyla sonuçlandıramamıştır) obje için oluştururlan destructor çağrılamamaktadır. Sonuç olarak bellek taşması oluşur.

Örnek vermek gerekirse;

struct foo
{
   foo() { std::cout << "foo constructed" << std::endl; }
   ~foo() { std::cout << "foo destroyed" << std::endl; }
};

struct bar
{
   bar() { throw std::exception("error in bar!"); }
};

struct foobar
{
   foobar()
      : m_foo(new foo()),
        m_bar()        
   {
      std::cout << "foobar constructed" << std::endl;
   }

   ~foobar()
   {
      std::cout << "foobar destroyed" << std::endl;
   }

private:
   foo* m_foo;
   bar m_bar;
};

int main()
{
   try
   {
      foobar fb;
   }
   catch (std::exception const & e)
   {
      std::cout << e.what() << std::endl;
   }
}

Bu aşağıdaki sonucu verecektir.

foo constructed
error in bar!

foo objesini başarıyla tahrip edebilmek (destroy) için try-catch aşağıdaki gibi kullanılabilir.

foobar()
  try
  : m_foo(new foo()),
    m_bar()
{
  std::cout << "foobar constructed" << std::endl;
}
catch (...)
{
  delete m_foo;
  throw;
}

Bu durumda kodun çıktısı şu şekilde olacaktır;

foo constructed
foo destroyed
error in bar!

try-cacth yerine akıllı pointer kullanımı da mümkündür. Aşağıdaki kod da aynı sonucu verecektir.

struct foobar
{
   foobar()
      : m_foo(std::make_unique<foo>()),
        m_bar()
   {
      std::cout << "foobar constructed" << std::endl;
   }

   ~foobar()
   {
      std::cout << "foobar destroyed" << std::endl;
   }

private:
   std::unique_ptr<foo> m_foo;
   bar m_bar;
};

İsimsiz “namespace” kullanımı

Namespace deyimi kullanımına popüler programlama dillerinde artık çok sık rastlıyoruz. Türkçeye “isim uzayı” veya “isim alanı” olarak tercüme edilen namespace deyiminin C++ dilinde genel kullanımı aşağıdaki şekildedir:

namespace namespaceAdi
{
     /*
     * sınıflar
     * fonksiyonlar
     * değişkenler
     */
}

Burada konumuz isim verilmeden namespace terimi (unnamed namespace veya anonymous namespace) nasıl ve neden kullanılır ona bakacağız.

Örneğin aşağıda namespace bloğunda her hangi bir isim görmüyoruz.

namespace {
    int foo() { return 0xf00; }
}

Yukarıdaki ifade aslında aşağıdaki ifadeye eşdeğerdir.

static int foo() { return 0xf00; }

Adından da anlaşılacağı gibi namespace bir isim vermeden tanımlanır ve sadece dahili bir bağlantı sunmak için kullanılır. Yani isimsiz bir namespace tanımı içindeki ifadeler sadece o kod dosyasında kalır, diğer dosyalardakilerle karışmaz. Daha iyi anlamak için bir örnek verelim.

foo.cpp ve bar.cpp kod dosyalarında tanımlanmış aynı isimde print fonksiyonları yazdığımız düşünelim.

// foo.cpp
void print(std::string message)
{
   std::cout << "[foo]" << message << std::endl;
}

// bar.cpp
void print(std::string message)
{
   std::cout << "[bar]" << message << std::endl;
}

Bunu VC++ compiler ile derleğimizde aşağıdakine benzer bir hata alacaksınız.

foo.obj : error LNK2005: “void __cdecl print(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)” (?print@@YAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in bar.obj
cpp_test.exe : fatal error LNK1169: one or more multiply defined symbols found

Aynı isimde iki fonksiyon veya başka tanımlamalar yapmak istediğinizde dosyalardan birindeki tanımı isimsiz namespace içine almanız sorunu çözecektir.

namespace
{
   void print(std::string message)
   {
      std::cout << "[bar]" << message << std::endl;
   }
}

void run()
{
   print("çalışıyor...");
}

Deha fazla detay için şuradaki videoya göz atınız; https://www.youtube.com/watch?v=HrFtpSH-Eso

“L-value” Üçlü koşul dizilimi

Kodları daha kısa ve anlaşılır kılmak için son zamanlarda popüler olan üçlü (ternary) koşul operatörleri C++ içinde de kullanılabiliyor.

Örnek vermek gerekirse;

auto a = 12;
auto b = 42;

auto max = a >= b ? a : b;

Yukarıdaki kod aslında aşağıdaki ile aynı sonucu verir. Yani basit bir ifade ile max değişkenine atanacak değer a değişkeninin b değişkenine eşit veya büyük olmasına bağlı olarak ya a yada b olarak belirlenir.

auto max = a;
if (b > a) max = b;

Bununla birlikte, üç koşullu operatör aynı zamanda, atama işleminin sol tarafındaki bir L-value (L-değeri) olarak da kullanılabilir.

c % 2 == 0 ? a : b = 1;

veya

c % 2 == 0 ? a : b = a >= b ? -a : -b;

L-value nedir detay için tıklayınız (İng.)

 

Etiketler
Daha Fazla Göster
Başa dön tuşu
Kapalı
Kapalı

Reklam Engelleyici Algılandı

Please turn the Add Blocker off as you may see there is no annoying ads in the blog.