destructor ya public virtual ya da protected non-virtual olmalı

Necati Ergin
2 min readJul 13, 2021

Başlık başlangıçta size biraz anlamsız gelmiş olabilir. Bu cümle kalıtımda taban sınıf olarak kullanılacak sınıfların sonlandırıcı işlevlerine (destructors) ilişkin Herb Sutter tarafından formüle edilmiş ve genel kabul görmüş bir ilkeyi anlatıyor. Cümlemizin ingilizcesini yazalım önce:

A base class destructor should be either public and virtual or protected and nonvirtual

Bir taban sınıfın sonlandırıcı işlevi ya public ve sanal olmalı ya da protected ve sanal olmamalı. Önce taban sınıfın sonlandırıcı işlevinin neden sanal olması gerektiğini bir hatırlayalım: Taban sınıf arayüzüyle kullanılacak bir işlevin çok biçimli (polymorphic) davranması gerekiyorsa sanal olması gerektiğini biliyorsunuz. Taban sınıfın bir işlevi sanal olmasa da kendisi işin bir kısmını sanal bir işleve delege ediyorsa yine çok biçimli bir davranış söz konusu. Dinamik bir nesnenin delete edilmesi de çok biçimli olarak taban sınıf arayüzüyle gerçekleştirilebilecek ise bu durumda taban sınıfın sonlandırıcı işlevi de sanal olmalı. C++ dilinin kuralları da bunu gerektiriyor. Bjarne Stroustrup bir makalesinde bu konuyu şöyle açıklıyor:

Hangi durumda bir sınıfın sonlandırıcı işlevini sanal yapmalıyım? Sınıfın en az bir sanal işlevi var ise. Bir sınıfın bir sanal işleve sahip olması kendisinden kalıtım yoluyla elde edilecek sınıflar için bir arayüz rolü oynayacağı anlamına geliyor. Bu da türemiş sınıf türünden nesnelerin taban sınıf göstericileri ile delete edilebileceği anlamına geliyor. Aşağıdaki kodu inceleyelim:

#include <iostream>class Base {
public:
//
~Base()
{
std::cout << "~Base\n";
}
};
class Der : public Base
{
public:
~Der()
{
std::cout << "~Der\n";
}
};
int main()
{
Base *p = new Der;
delete p; //Base::~Base sanal olmalı
}

Yukarıdaki main işlevi içinde oluşturulan dinamik Der nesnesinin adresini Base türünden bir gösterici değişken tutuyor. delete işleminin p adresi ile yapılması C++ dilinin kurallarına göre tanımsız davranış (undefined behavior). Yukarıdaki kodu derleyip çalıştırdığınızda Base sınıfının sonlandırıcı işlevinin çağrıldığını ancak Der sınıfının sonlandırıcı işlevinin çağrılmadığını göreceksiniz. Der sınıfı sonlandırıcı işleviyle bazı kaynakları geri veriyor ise, çağrılmayan sonlandırıcı işlev kaynak sızıntısına yol açacak. Aynı kodu bir de taban sınıfın sonlandırıcı işlevini sanal yaparak yeniden derleyin ve çalıştırın:

class Base {
public:
//
virtual ~Base()
{
std::cout << "~Base\n";
}
};
class Der : public Base
{
public:
~Der() override
{
std::cout << "~Der\n";
}
};

Eğer taban sınıf türünden bir gösterici ile delete işleminin yapılmaması gerekiyor ise taban sınıfın sonlandırıcı işlevinin sanal olmaması ve sınıfın protected bölümünde bildirilmesi en doğru seçim:

class Base {
public:
//
protected:
~Base();
//
};

Bu durumda taban sınıf göstericisi ile (yanlışlıkla) yapılacak delete işlemleri sentaks hatası durumuna düşerken türemiş sınıf sonlandırıcı işlevinin taban sınıf sonlandırıcı işlevini çağırması geçerli bir kod oluşturacak.

--

--