ProgramlamaC++ sistem/altyapı geliştiricisi

Fonksiyonlar ve değişkenlerin miras alındığında nasıl arandığını (isim arama, kapsam) ve özellikle sanal miras durumunda hiyerarşilerde isimlerle ilgili belirsizliklerin nasıl ortaya çıktığını anlatın. Bu çatışmalara nasıl çözüm bulunur?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap

C++ dilinde isimlerin arama sırası, isim çözümleme (name lookup) kurallarıyla düzenlenir; bu kurallar kapsam (scope), gizleme (hiding) ve miras almanın özelliklerini dikkate alır. Ana noktalar:

  • Bir yöntem çağrıldığında/değişkene erişildiğinde, arama türetilmiş kapsamdan temel sınıfa doğru yapılır.
  • Çoklu miras durumunda, isim her iki temel şubede de mevcutsa belirsizlik (ambiguity) oluşur.
  • Sanal miras, tekrarı önlemeye yardımcı olur (diamond problem), ancak yine de isimlerin açık bir şekilde yönetilmesini gerektirir.

Çatışma çözme yöntemleri:

  • Alt sınıfta gizlenmiş yöntemler için using kullanılabilir:
class Base { public: void foo(int) {} }; class Derived : public Base { public: using Base::foo; void foo(double) {} // Aşırı yüklenmiş versiyon };
  • Aynı isimli değişkenler veya fonksiyonlar için:
    • Temel sınıfın adını açıkça belirtmek gerekir: Base::x veya Base::foo().

Diamond mirası durumundaki belirsizlik örneği:

struct A { int x; }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C { void test() { x = 42; } // TAMAM: x bir tane, sanal sayesinde };

Kara Şaka Sorusu

Eğer temel sınıfta foo(int) yöntemi tanımlıysa ve türetilmiş sınıfta foo(double) varsa, türetilmiş sınıfın nesnesinden foo(int) erişilebilir mi?

Cevap: Hayır, foo(double) yöntemi temel sınıftaki tüm aşırı yüklenmiş foo versiyonlarını "gizler". foo(int)'ye erişmek için açıkça using Base::foo yazmak veya çağrıda Base::foo kullanmak gerekir.

Örnek:

class Base { public: void foo(int) { } }; class Derived : public Base { public: void foo(double) {} }; Derived d; d.foo(3); // HATA! foo(int) görünür değil

Kullanım:

class Derived : public Base { public: using Base::foo; void foo(double) {} }; d.foo(3); // Her şey çalışıyor

Bilgi eksikliğinden kaynaklanan gerçek hata örnekleri


Hikaye

Çoklu miras alan bir projede, fo() yöntemleri aynı olan iki temel sınıftan miras alan bir sınıf ortaya çıktı. Yazarlar sanal miras kullanmadılar, bu yüzden temel sınıfın iki kopyası oluşturuldu — tüm çağrılar belirsiz hale geldi ve derleyici kodu derlemeyi reddetti. Virtual inheritance'in uygulanması ve çağrıda Base::foo() yazılması ile çözüm bulundu.


Hikaye

Bir grafik motorunda bir alt sınıf kendi draw() yöntemini tanımladı ve temel sınıftaki draw() için using yapmadı. Kodun döndürülmesi (Refactor) sırasında yanlış versiyon draw() çağrıldı — arayüzün bazı kısımları çizilemedi. Hata, sınıf yapısının derinlemesine incelenmesinden sonra bulundu.


Hikaye

Yeni bir derleyiciye geçişte, belirsizlik nedeniyle derleme imkânsız hale geldi. Daha önce çalışan uygulama, farklı include zincirlerini göz önünde bulundurarak, farklı isim setlerine yol açıyordu.