final bağlamsal anahtar sözcüğü

final C++11 ile gelen bir başka bağlamsal anahtar sözcük.  İki yerde kullanılıyor:
final anahtar sözcüğü ile bir sınıfın türetme hiyerarşisindeki son sınıf olduğu bildiriliyor: Bu durumda final olarak bildirilen sınıftan kalıtım yoluyla yeni sınıflar elde edilemiyor:

Base sınıfından türetilen Der1 sınıfı final olarak nitelenmiş. Bu durumda Der1 sınıfından Der2 sınıfının türetilmesi artık geçerli değil.

final anahtar sözcüğü bir sanal işlevi nitelediğinde bu sanal işlevin  türemiş sınıflar tarafından ezilemeyeceğini belirtiyor. final olarak belirtilen bir sanal işlevin ezilmesi derleyici tarafından sentaks hatası olarak değerlendiriliyor:

Base sınıfından türetilmiş Der1 sınıfı Base sınıfının func isimli sanal işlevini ezerken final anahtar sözcüğünü kullanmış. Artık Der1 sınıfından kalıtım yoluyla elde edilecek sınıflar Der1 sınıfının func sanal işlevini ezme hakkına sahip değiller. Der2 sınıfının func işlevini ezmesi sentaks hatası.

Sanal olmayan bir işlevin final olarak belirlenmesi geçerli değil:

override sözcüğü gibi final sözcüğü de bir bağlamsal anahtar sözcük. Yani bu bağlam dışında kullanıldığında anahtar sözcük niteliğinde değil. Böylece final sözcüğünü bir isim olarak kullanan eski kodlar yeni derleyicilerde derlendiğinde geçerlilik korunuyor:

Neden final sınıflar?

Bir sınıfı kendisinden kalıtım yoluyla yeni sınıflar üretilmeyeceği niyetiyle oluşturabiliriz. Böyle bir sınıftan yanlışlıkla bir sınıfın türetilmesi çalışma zamanı hatalarına neden olabilir. Oluşturduğumuz sınıfı final olarak tanımladığımızda artık bu sınıftan kalıtım girişimi derleyicinin kontrolune tabi olduğu için hata riski ortadan kalkar. Peki neden bir sınıftan kalıtım yoluyla başka bir sınıf elde edilmesine engel olmak isteyelim? Bunun birden fazla nedeni olabilir. Örneğin STL kaplarında olduğu gibi, bir sınıfın çalışma zamanı çok biçimliliğini kullanmak istemediğini düşünelim. Bu durumda doğal olarak sınıfın sonlandırıcı işlevi sanal yapılmayacaktır. Böyle bir  sınıftan isteğimiz dışında kalıtım yoluyla yeni bir sınıf oluşturulduğunda, dinamik türemiş sınıf nesnelerinin taban sınıf göstericileri yoluyla delete edilmesi, kaynak sızıntısına ve buna bağlı olarak çalışma zamanı hatasına neden olabilir:

Yukarıdaki koda bakalım. Base sınıfının sonlandırıcı işlevi sanal değil. Der sınıfı Base sınıfından türetilmiş. main işlevi içinde oluşturulan dinamik Der nesnesinin adresi Base sınıfı türünden bir göstericiye ilk değer olarak verilmiş. Dinamik nesnenin hayatını sonlandırmak için

deyimi yazılmış. Bu durumda Base sınıfının sonlandırıcı işlevi çağrılır ama Der sınıfınının sonlandırıcı işlevi çağrılmaz. Eğer Der sınıf nesneleri Der kurucu işlevi birtakım kaynakları ediniyor ise bu kaynakları geri verecek olan Der sınıfının sonlandırıcı işlevidir. Çağrılmayan sonlandırıcı işlev yüzünden kaynaklar geri verilmeyecektir. Oysa Base sınıfının sonlandırıcı işlevi sanal olsaydı Der sınıfının sonlandırıcı işlevi çağrılacak ve kaynaklar geri verilmiş olacaktı. Büyük bir programda böyle bir hatanın yapılma olasılığı çok düşük değildir. Base sınıfını final belirleyicisi ile türetmeye tamamen kapatarak böylesi hataları önleyebiliriz.
Bir sınıfın final olması derleyicinin eniyileştirme (optimizasyon) olanaklarını da genişletebilir.

Neden final İşlevler?
Bir taban sınıfın kendisinden türetilecek sınıflara bir işlevin hem arayüzünü hem de kodunu vermek istediğini düşünelim. Bunun doğal yolu taban sınıfın bu üye işlevi sanal yapmamasıdır. Şimdi
söz konusu taban sınıfın da başka sınıfından türetilmiş olduğunu ve kendi taban sınıfının sanal işlevini ezdiğini düşünelim.

Der sınıfı taban sınıfı olan Base‘in sanal func işlevini ezmiş. Ancak Der sınıfı kendisinden kalıtım yoluyla elde edilecek sınıfların da kendi sağladığı kodu kullanmasını istiyor. Der sınıfının func işlevi taban sınıfın sanal işlevini ezdiği için artık kendisi de sanal. Yani artık Der sınıfından türetilecek sınıflar da bu işlev için kendileri kod sağlayabilirler. Dilin kuralları buna engel olmuyor. C++11 öncesinde birçok programcı bu durumda yorum satırı ile işlevin final olduğu belirtiyorlardı:

C++11’de böylesi bir durumda final bağlamsal anahtar sözcüğü ile bir hata riskini ortadan kaldırıyoruz.İşlevi final olarak belirlediğimizde derleyici bu işlevin Der sınıfından türetilecek sınıflar tarafından ezilmesine izin vermiyor.

Bir işlevi final yapmamızın bir başka nedeni de yine eniyileme. final bir işleve yapılan bazı çağrılar için derleyici sanal işlev tablosundan hareketle hangi işlevin çağrıldığının programın çalışma zamanında belirleneceği bir kod oluşturmak yerine çağrılan işlevin hangisi olduğunu derleme  zamanında anlayarak daha verimli bir kod üretebiliyor. Derleyicilerin böyle durumlarda uyguladığı eniyileme tekniğine ingilizcede “devirtualization” deniyor. Eğer koşullar uygunsa derleyici ilgili işleve yapılan çağrıyı inline olarak da işleyebilir.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun
Eklemek istediğiniz kodları lütfen aşağıdaki “pre” kodları arasında yazınız.
<pre class="lang:c++ decode:true ">
--yazacağınız kodlar--
</pre>
(buradan kopyalayarak kullanabilirsiniz)