Sanal fonksiyonlar, türetilmiş sınıf nesnelerinin, temel sınıfın işaretçileri veya referansları üzerinden işlenebilmesini sağlayan polimorfizmi gerçekleştirir. Sanal bir fonksiyonu tanımlamak için virtual anahtar kelimesi kullanılır:
class Base { public: virtual void foo() { std::cout << "Base::foo"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo"; } };
Base* türündeki bir işaretçi aracılığıyla foo() çağrısı, Derived nesnesine işaret ediyorsa Derived'teki implementasyonu çağırır.
Soyut sınıf, en az bir tamamen sanal fonksiyon içeren sınıftır:
class Interface { public: virtual void process() = 0; // tamamen sanal fonksiyon };
Sanal miras, elmas miras problemine (diamond problem) çözüm sunar:
class A { }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { };
Bu, D nesnesinde sadece bir tane temel sınıf A örneği olacağını garanti eder.
Sanal bir yıkıcı ile normal bir yıkıcı arasındaki fark nedir? Miras için tasarlanmışsa, yıkıcının sanal olması gerekir mi?
Cevap: Sanal bir yıkıcı, temel tipteki bir işaretçi üzerinden silme işlemi yapıldığında, türetilmiş sınıfın yıkıcısının çağrılmasını garanti eder. Bu, kaynakların doğru bir şekilde serbest bırakılması için önemlidir.
class Base { public: virtual ~Base() {} };
Eğer yıkıcı sanal değilse, delete BasePtr; çağrısı yapıldığında yalnızca Base'in yıkıcısı çağrılacak ve miras alınan alanların kaynakları serbest bırakılmayacaktır.
Hikaye
Farklı araçlar için ortak bir sınıf arayüzü olan büyük bir finansal sistemde, sanal yıkıcı tanımlanmamıştı. Bir miras alanda dinamik bir kaynak kullanılıyordu. Temel tipteki bir işaretçi aracılığıyla nesne silindiğinde bellek sızıntısı oldu, bu yalnızca sanayi yükleme aşamasında bulundu.
Hikaye
Ekip, sanal miras kullanmadan çoklu miras kullandı. D sınıfı, ara sınıflar aracılığıyla A'yı iki kez miras aldı. Bu, durumun kopyalanmasına ve A'nın üyelerine erişimde hatalara yol açtı. Sadece statik analiz araçlarıyla yapılan bir denetimden sonra düzeltildi.
Hikaye
Bir günlük eklenti geliştirme projesinde, soyut bir sınıf kullanıldı ancak yıkıcı sanal yapılmadı. Arayüz işaretçisi aracılığıyla eklentiler silindiğinde, çağrılmayan türevlerin yıkıcılarıyla ilgili belirgin olmayan bağımlılıklar ve hatalar gözlemlendi. Sorun, kaynak havuzunu kapsıyor ve kaynak sızıntılarına yol açıyordu.