Mülakat Soruları | Kod çıktısı -II


Mülakatlarda soruları bir kaç kısımdan oluşmaktadır. Bunlar arasında verimli kod yazma veya yazılan kodun çıktısının yorumlamada vardır. Bu nedenle bazı temel algoritmaları bilmek daha avantajlı ve başka problemler için ufuk açıcı olacaktır.

Çift sayıları veya tek sayıları ekrana yazdıran programı daha verimli olarak nasıl yazarsınız? (Mod işlemi dışında)





Yukarıdaki kod parçası bitsel işlem yaparak çift sayı/tek sayı kontrolü yapmaktadır. Teknik olarak bir sayının çift veya tek olması 0. bitinin değerine göredir. Bit 0 ise çift 1 ise tek sayıdır. Karşınıza çıkacak en basit sorulardan biri olabilecek olan bu soruyu kaçırmamanız dileğiyle.

Mülakat Soruları | Kod çıktısı -I


C++'da int gibi primitive bir veri türüne ilk değer verme işlemi int val = 3;  syntax'ının dışında farklı yöntemlerle de yapılabilir. Aşağıda resimde görüleceği üzere bizim bildiğimiz ilk değer verme dışında 3 farklı biçimde ilk değer verilebilir.



Buradan asla şu sonucu çıkartmayalım:
  • C++'da int türü de string gibi sınıf türünden bir veri tipi midir? Hayır, bu notasyona sadece template mekanizması içinde bir ihtiyaçtan dolayı gerek duyulmuştur. Yoksa int türü C++ için de C'de olduğu gibi bir veri türüdür. 
Bu açıdan bakılırsa yukarıda 1,2,3 nolu deyimlerin her biri primitive bir tür olan int'e ilk değer verme işlemidir. Ve doğrudur.

Rule INT#030 - Argument is pointer or array


Fonksiyon bildirimi yapılırken parametre olarak int *ptr demekle int ptr[] demek arasında pratikte bir fark yoktur. Buna rağmen uygulamada kodu okuyan - kodun bakımını yapan açısından bir fark vardır. Aşağıda olduğu gibi Foo fonksiyonunu gören kişi bu fonksiyonun dizi adresi istediğini anlayacaktır.

parametre olarak dizi adresi isteyen fonksiyon
Aşağıda olduğu gibi Foo fonksiyonunu gören kişi ise bu fonksiyonun int * türünden bir adres istediğini anlayacaktır. Bu nedenle kod yazarken bu standarta dikkat etmek kod okuyan -bakımını yapan açısından faydalı olacaktır.

parametre olarak pointer isteyen fonksiyon

Fonsiyon çağrısı yapılırken aşağıdaki gibi dizi adresi isteyen bir fonksiyona o türden bir pointer gönderilmesi istenen bir durum değildir. Kod çalışmasında bir sıkıntı olmayacaktır ama kodun bakımını yapacaklar için dizi adresi isteyen fonksiyona dizi adresi pointer isteyen fonksiyona da pointer gönderilmesi daha doğru olacaktır.


Dizi adı da bir adres belirttiği halde yukarıda anlatıldığı gibi bir ayrıma gitmek daha anlaşılır kodlar yazılmasını sağlayacaktır.

Rule INT#026 -


Türeyen sınıf içinde base sınıfla aynı isme sahip bir fonksiyon varsa dikkat edilmesi gereken bazı noktalar vardır:

  • Base sınıf virtual ise override edip sanallık mekanizmasına dahil mi olunacak?
  • Fonksiyon virtual değilse türeyen (derived) sınıfta using bildirimi yapılarak overload mı edilecek?
  • Hiçbir şey yapılmazsa ne gibi sorunlarla karşılaşılacak?

Biz bu yazımızda 2. ve 3. kısımlara yoğunlaşacağız. Bu noktada yapılması gerekenler ve dikkat edilmesi gereken noktalar nelerdir? Aşağıda görülen kod parçasında base sınıfta char türünden parametresi olan bir fonksiyon, türeyen sınıfta ise int türünden parametresi olan bir fonksiyon bulunmaktadır. Main içinden char türünden parametresi olan fonsiyon çağrısı yapıldığında türeyen sınıfın int türünden parametresi olan cry fonksiyonu çağrıldığı görülmektedir.


Burada fonksiyon arama sırası şöyle işlemektedir. Önce derived class içine bakılır ve cry isimli bir fonksiyon bulunursa o noktadan sonra arama sonlandırılır ve parametreler uygun değil fakat dönüştürülebiliyor ise otomatik dönüştürme yapılır ve yukarıdaki gibi çağrılır. Peki otomatik bir arguman dönüştürme gerçekleşmiyor ise ne yapılır? Uygun parametreli fonksiyon mu aranacak? Tabi ki HAYIR. Aşağıda görüldüğü gibi hata verecektir.


Peki animal sınıfı içine uygun bir fonksiyon koyarsak hata ortadan kalkacak ve bu fonksiyon mu çağrılacak? Yukarıda bahsettiğimiz gibi gene hata verdiği ve base sınıf içinde bir arama yapmadığı görülecektir.



Aşağıda da  ise bu durum nasıl halledildiği görülmektedir:



Türeyen sınıf içine fonksiyonun using bildirimi yapılarak problem ortadan kaldırılmış olur. Overload çözülme (resolution) işlemi farklı sınıflar arasında kalıtım sözkonusu olsa bile doğrudan mümkün değildir. Bunun için yukarıdaki gibi using anahtar kelimesi kullanılması gerekmektedir.



Rule INT#027 - Türeyen sınıfta oveload edilmiş sanalllık mekanizması (base class's virtual function explicitly as virtual)


C++'da türemiş sınıflar (derived class) virtual fonksiyonu override ederek sanallık mekanizmasına dahil olurlar ve override ettiği fonksiyonda virtual olur.

override edilmiş cry sanal fonksiyonu

Yukarıda görüldüğü gibi Animal sınıfının (base class) cry() sanal fonksiyonu Dog sınıfı tarafından override edilerek Dog sınıfı da sanallık mekanizmasına dahil edilmiştir. Standartlar bize açık açık bildirmesekte Dog sınıfının cry() fonksiyonunun sanal olacağını belirtir. Ama bizim bunu explicit olarak yazmamız kod okuyan açısından daha doğru olacaktır.

Kodu bu hale getirmek daha doğru olacaktır

Aynı mantık bir sonraki türetme içinde geçerlidir.WathDog sınıfı da cry fonksiyonunu override ederek sanallık mekanizmasına dahil olmuştur ve bu fonksiyonu virtual void cry() şeklinde tanımlaması daha doğru olacaktır. 







Rule INT#0XX - Class Interface


Rule INT#002 - Sınıfın sabit olmayan üye elemanlarının private yapılması (Declare non constant data member private)

Bir sınıf tasarımı yapılırken sınıfın veri elemanlarına doğrudan erişim olması çok doğru bir tasarım mantığı değildir. Bu amaçla veri elemanlarına erişim veya değiştirme gibi durumlar için GetValueXXX() ile SetValueXXX() gibi iki fonksiyon tasarlanması daha doğru olacaktır. Constant veri elemanları için bu kural geçerli olmasa da (dışarıdan değiştirme mümkün olmadığından dolayı) biz kurala constant veri elemanlarını da dahil edip onun için sadece GetValueXXX() fonksiyonunu sınıfa eklememiz daha iyi bir tasarım olur.

Rule INT#015 - Üye fonksiyonların aşırı yüklenmesi ( Class member function overloading )


Class içinde üye fonksiyonlar aşırı yükleme yapılırken overload edilmiş fonksiyonlar'ın hepsinin amacının/yaptıkları işlerin aynı olmasına dikkat edilmelidir. Bu yazılımsal bütünlüğün sağlanması ve kod karmaşıklığının önüne geçilmesini sağlayacaktır. Kodun bakımını yapan için de daha verimli olunmasını sağlayacaktır.


Rule INT#029 - Boolean type

C++ standartlarında boolean type bir veri tipi olarak gelmektedir. Bu nedenle mümkün olan yerlerde true-false değerleri kullanmak C'de olduğu gibi standartlarda olmayan boolean type için 0-1 'i kulllanmaktan daha doğru kod yazılmasını sağlayacaktır. Ve kod bakımının daha doğru olmasını sağlayacaktır.


Rule INT#021 - Sınıf türünden arguman geçilmesi (Pass arguments of class types by reference or pointer)


Bir fonksiyonun base sınıf türünden bir parametresi varsa bu durumda dikkat edilmesi gereken bazı noktalar vardır. Aşağıda verilen base ve derived sınıf yapısı üzerinden bu noktaları anlatmak gerekirse:


Aşağıda yer alan AnimalCry() fonksiyonunun Animal türünden parametresi vardır. Fakat bu fonksiyona arguman olarak derived class türünden bir nesne gönderilir ise ne olur? Derived sınıf türünden bir nesne gönderilirse object slicing durumu oluşmakta ve Dog sınıfının cry fonksiyonu çağrılmayacaktır.


Bu durumu engellemek için kod stadartları fonksiyonun parametresi referans (aşağıda görüldüğü gibi):



Veya pointer olması gerekmektedir. Bu sayede yukarıda ve aşağıda görüleceği üzere istenilen sınıfın üye fonksiyonları çağrılacaktır.


Not: Zaten bir sınıfı parametre olarak göndermek verimli kod standartı açısından kabul edilebilir bir yöntem değildir. Ama sanallık mekanizması ve türetme sözkonusu olduğunda bu durumda verimli kod yazmanın yanında hatasız kod yazmak için çok daha gerekli olan bir yöntemdir.

Rule INT#001 - Bir Parametreli Constructor (Single-argument constructors)

C++ ile kodlama yapılırken standartlar gereği bir parametreli constructor tanımlanırken (copy constructor dışında) constructor'ın explicit olarak tanımlanması özel bir neden yoksa daha doğru olacaktır. Peki neden böyle bir standart vardır? Oluşabilecek hatalı çalışmalara açıklık getirirsek neden bir standart haline getirildiği açıklığa kavuşmuş olacaktır:

conversion constructor 

Yukarıdaki kod nasıl çalıştı?

Yukarıda nesnenin yaratılması sırasında bir parametreli constructor'ın çağrıldığı görülmektedir. Ama neden? Burada sınıf içinde implicit bir tür dönüştürme işlemi gerçekleşmiş ve bir parametreli constructor çağrılarak nesne yaratılmıştır. Bu nedenle bir parametreli bu tür constructor'lara conversion constructor'da denilmektedir. Böyle bir durumun dikkate alınmaması ileride gözden kaçacak bir çok problemin ortaya çıkmasına neden olabilecektir. Yukarıda bahsettiğimiz Fax f = 12;  kod parçasını aşağıda gözlemlediğimiz;

  • Fax  f(12);
  • Fax f{13}; 
ile benzer şekilde çalıştığı görülmektedir.


Bir parametreli constructor ile nesnenin yaratılması

Bir parametreli constructor ile nesnenin yaratılması

Yukarıda Fax f=12 ifadesinin geçerli bir kod olmasını engellemek için bir parametreli constructor'ın explicit olarak tanımlanması yeterli olacaktır.


explicit constructor



Sınıflar için implicit type conversion durumunu engellemek dikkat edilmesi gereken constructor türü bir parametreli (Single-argument constructors) constructorlardır. Constructor için explicit anahtar sözcüğü eklendikten sonra kodun derlenemediği yukarıda da görülebilmektedir. Bu kural copy constructor harici bir parametreli constructorlar için geçerlidir.