Tasarım perspektifinden işleri değişmez olarak işaretlemek genellikle yararlıdır. Aynı şekilde, const
derleyici koruma sağlar ve bir durumun değişmemesi gerektiğini final
belirtir, davranışın miras hiyerarşisinde daha fazla değişmemesi gerektiğini belirtmek için kullanılabilir.
Misal
Araçların oynatıcıyı bir yerden başka bir yere götürdüğü bir video oyunu düşünün. Tüm araçlar kalkıştan önce geçerli bir yere seyahat ettiklerini kontrol etmelidir (örneğin, yerdeki tabanın tahrip olmadığından emin olun). Bu kontrolün araçtan bağımsız olarak yapıldığından emin olmak için sanal olmayan arayüz deyimini (NVI) kullanmaya başlayabiliriz.
class Vehicle
{
public:
virtual ~Vehicle {}
bool transport(const Location& location)
{
// Mandatory check performed for all vehicle types. We could potentially
// throw or assert here instead of returning true/false depending on the
// exceptional level of the behavior (whether it is a truly exceptional
// control flow resulting from external input errors or whether it's
// simply a bug for the assert approach).
if (valid_location(location))
return travel_to(location);
// If the location is not valid, no vehicle type can go there.
return false;
}
private:
// Overridden by vehicle types. Note that private access here
// does not prevent derived, nonfriends from being able to override
// this function.
virtual bool travel_to(const Location& location) = 0;
};
Şimdi diyelim ki oyunumuzda uçan araçlarımız var ve tüm uçan araçların gerektirdiği ve ortak noktası olan şey, kalkıştan önce hangarın içinde bir güvenlik denetimi kontrolünden geçmeleri gerektiğidir.
Burada final
, tüm uçan araçların böyle bir incelemeden geçeceğini ve aynı zamanda uçan araçların bu tasarım gereksinimini ileteceğini garanti etmek için kullanabiliriz .
class FlyingVehicle: public Vehicle
{
private:
bool travel_to(const Location& location) final
{
// Mandatory check performed for all flying vehicle types.
if (safety_inspection())
return fly_to(location);
// If the safety inspection fails for a flying vehicle,
// it will not be allowed to fly to the location.
return false;
}
// Overridden by flying vehicle types.
virtual void safety_inspection() const = 0;
virtual void fly_to(const Location& location) = 0;
};
final
Bu şekilde kullanarak , sanal olmayan arayüz deyiminin esnekliğini, miras hiyerarşisinde (sonradan düşünüldüğünde bile, kırılgan temel sınıf sorununa karşı koyarken) sanal işlevlerin kendilerine eşit bir davranış sağlamak için genişletiyoruz. Ayrıca, var olan her uçan araç uygulamasını değiştirmeden, tüm uçan araç türlerini etkileyen bir sonraki düşünce olarak merkezi değişiklikler yapmak için kendimizi kıpırdatmak için oda satın alıyoruz.
Bu, bunun gibi bir örnek final
. Sanal üye işlevinin daha fazla geçersiz kılınmasının mantıklı olmadığı yerlerde karşılaşacağınız bağlamlar vardır - bunu yapmak kırılgan bir tasarıma ve tasarım gereksinimlerinizin ihlaline yol açabilir.
final
Tasarım / mimari açıdan faydalı olan yer burasıdır .
Optimize ediciye, sanal işlev çağrılarını (dinamik dağıtım yükünü ortadan kaldırarak ve genellikle daha da önemlisi, arayan ve arayan arasında bir optimizasyon engelini ortadan kaldırarak) devretmesini sağlayan bu tasarım bilgilerini sağladığından, bir optimize edicinin bakış açısından da yararlıdır.
Soru
Yorumlardan:
Nihai ve sanal neden aynı anda kullanılsın?
Hiyerarşinin kökündeki bir temel sınıfın bir işlevi hem virtual
ve hem de olarak tanımlaması mantıklı değildir final
. Hem derleyici hem de insan okuyucunun virtual
, böyle bir durumda basitçe kaçınarak kaçınılabilen gereksiz halkalardan atlamak zorunda kalacağı için bu bana aptalca geliyor . Ancak, alt sınıflar aşağıdaki gibi sanal üye işlevlerini devralır:
struct Foo
{
virtual ~Foo() {}
virtual void f() = 0;
};
struct Bar: Foo
{
/*implicitly virtual*/ void f() final {...}
};
Bu durumda, Bar::f
açıkça sanal anahtar sözcüğü kullanıp kullanmadığı Bar::f
sanal bir işlevdir. virtual
Anahtar kelime daha sonra bu durumda isteğe bağlı hale gelir. İçin mantıklı olabilir Yani Bar::f
olarak belirtilmesi final
bunun sanal bir fonksiyonu olsa, ( final
olabilir sadece sanal fonksiyonlar için kullanılabilir).
Ve bazı insanlar, stilistik olarak, Bar::f
bunun sanal olduğunu açıkça belirtmeyi tercih edebilir :
struct Bar: Foo
{
virtual void f() final {...}
};
Bana göre bu tür her ikisini de kullanmak yedekli ait olduğunu virtual
ve final
bu bağlamda (aynı şekilde aynı işlev için belirteçleri virtual
ve override
), ancak bu durumda tarzı meselesi. Bazı insanlar , dış bağlantı ile fonksiyon bildirimleri virtual
kullanmak gibi (burada extern
diğer bağlantı niteleyicilerinden yoksun olsa da) değerli bir şeyi ilettiğini fark edebilir .