Sanal işlev - Virtual function

Olarak nesne yönelimli programlama gibi dilde C ++ ve Nesne Pascal , bir sanal işlevi ya da sanal bir yöntem kalıtsal ve bir geçersiz kılınabilir fonksiyonu ya da yöntem olan dinamik bir gönderme kolaylaştırılır. Bu kavram, nesne yönelimli programlamanın (OOP) (çalışma zamanı) polimorfizm bölümünün önemli bir parçasıdır . Kısacası, sanal bir işlev yürütülecek bir hedef işlevi tanımlar, ancak derleme zamanında hedef bilinmeyebilir.

Java , PHP ve Python gibi çoğu programlama dili, tüm yöntemleri varsayılan olarak sanal olarak ele alır ve bu davranışı değiştirmek için bir değiştirici sağlamaz. Ancak bazı diller, yöntemlerin türetilmiş sınıflar tarafından geçersiz kılınmasını önlemek için değiştiriciler sağlar ( Java ve PHP'deki final anahtar sözcüğü gibi ).

Amaç

Sanal işlev kavramı aşağıdaki sorunu çözer:

Olarak nesne yönelimli programlama bir taban sınıfından türetilmiş bir sınıf devralır, türetilmiş sınıf bir amacı ile de ifade edilebilir, işaretçi ya da referans yerine türetilmiş bir sınıf tipi baz sınıfı türü. Türetilmiş sınıf tarafından geçersiz kılınan temel sınıf yöntemleri varsa, bu tür bir başvuru veya işaretçi tarafından fiilen çağrılan yöntem, işaretçi veya başvurunun bildirilen türüne göre 'erken' (derleyici tarafından) veya 'geç' (yani, dilin çalışma zamanı sistemi tarafından), nesnenin gerçek türüne göre başvurulur.

Sanal işlevler 'geç' çözülür. Söz konusu işlev temel sınıfta 'sanal' ise, en çok türetilmiş sınıfın işlevin uygulaması, belirtilen işaretçi veya başvuru türünden bağımsız olarak başvurulan nesnenin gerçek türüne göre çağrılır. 'Sanal' değilse, yöntem 'erken' çözümlenir ve işaretçinin veya referansın bildirilen türüne göre seçilir.

Sanal işlevler, bir programın, kodun derlendiği anda mutlaka var olmayan yöntemleri çağırmasına izin verir.

C++'da sanal yöntemler , temel sınıftaki işlevin bildirimine anahtar sözcük eklenerek bildirilir . Bu değiştirici, türetilmiş sınıflardaki bu yöntemin tüm uygulamaları tarafından devralınır, yani birbirlerini aşmaya ve geç bağlanmaya devam edebilirler. Ve temel sınıfa ait yöntemler sanal yöntemi çağırsa bile, bunun yerine türetilmiş yöntemi çağıracaklar. Aşırı yükleme , bir sınıftaki iki veya daha fazla yöntemin aynı yöntem adına ancak farklı parametrelere sahip olması durumunda oluşur. Geçersiz kılma , aynı yöntem adı ve parametrelere sahip iki yönteme sahip olmak anlamına gelir. Aşırı yükleme, aynı zamanda işlev eşleştirme ve geçersiz kılma, dinamik işlev eşleme olarak da adlandırılır. virtual

Örnek

Hayvanın Sınıf Şeması

Örneğin, bir temel sınıf Animalsanal bir işleve sahip olabilir Eat. Alt sınıf , alt sınıftan farklı şekilde Llamauygulanır , ancak bir kişi Animal olarak adlandırılan herhangi bir sınıf örneğini çağırabilir ve belirli alt sınıfın davranışını alabilir . EatWolfEatEat

class Animal {
 public:
  // Intentionally not virtual:
  void Move(void) {
    std::cout << "This animal moves in some way" << std::endl;
  }
  virtual void Eat(void) = 0;
};

// The class "Animal" may possess a definition for Eat if desired.
class Llama : public Animal {
 public:
  // The non virtual function Move is inherited but not overridden.
  void Eat(void) override {
    std::cout << "Llamas eat grass!" << std::endl;
  }
};

Bu, bir programcının, listede ne tür bir hayvan olabileceğini, her bir hayvanın nasıl yediğini veya tüm olası kümenin ne olduğunu bilmeye gerek duymadan, Animalsırayla her birine yemesini (çağırarak Eat) söyleyerek , sınıfın nesnelerinin bir listesini işlemesine izin verir . hayvan türleri olabilir.

Yukarıdaki örneği C'ye uygulayarak sanal fonksiyonların nasıl çalıştığını daha iyi görebiliriz.

#include <stdio.h>

/* an object points to its class... */
struct Animal {
    const struct AnimalClass * class;
};

/* which contains the virtual function Animal.Eat */
struct AnimalClass {
    void (*Eat)(struct Animal *); // 'virtual' function 
};

/* Since Animal.Move is not a virtual function
   it is not in the structure above. */
void Move(struct Animal * self)
{
    printf("<Animal at %p> moved in some way\n", (void *) self);
}

/* unlike Move, which executes Animal.Move directly,
   Eat cannot know which function (if any) to call at compile time.
   Animal.Eat can only be resolved at run time when Eat is called. */
void Eat(struct Animal * self)
{
    const struct AnimalClass * class = *(const void **) self;
    if (class->Eat) 
        class->Eat(self); // execute Animal.Eat
    else
        fprintf(stderr, "Eat not implemented\n");
}

/* implementation of Llama.Eat this is the target function 
   to be called by 'void Eat(struct Animal *).' */
static void _Llama_eat(struct Animal * self)
{
    printf("<Llama at %p> Llama's eat grass!\n", (void *) self);    
}

/* initialize class */
const struct AnimalClass Animal = {(void *) 0}; // base class does not implement Animal.Eat
const struct AnimalClass Llama = {_Llama_eat};  // but the derived class does

int main(void)
{
   /* init objects as instance of its class */
   struct Animal animal = {& Animal};
   struct Animal llama = {& Llama};
   Move(& animal); // Animal.Move
   Move(& llama);  // Llama.Move
   Eat(& animal);  // cannot resolve Animal.Eat so print "Not Implemented" to stderr
   Eat(& llama);   // resolves Llama.Eat and executes
}

Soyut sınıflar ve saf sanal işlevler

Bir sanal işlevi ya da saf sanal bir yöntem türetilen sınıf değilse türetilmiş bir sınıfı tarafından uygulanan gereken bir sanal fonksiyonudur özet . Saf sanal yöntemler içeren sınıflar "soyut" olarak adlandırılır ve doğrudan başlatılamazlar. Bir alt sınıf bir bir soyut sınıfın tüm kalıtsal saf sanal yöntemleri bu sınıf veya bir üst sınıfı tarafından uygulanmıştır keşke doğrudan örneği olabilir. Saf sanal yöntemlerin tipik olarak bir bildirimi ( imza ) vardır ve tanımı yoktur ( uygulama ).

Örnek olarak, soyut bir temel sınıf MathSymbol, saf bir sanal işlev doOperation()ve somut uygulamalar sağlamak için türetilmiş sınıflar Plusve Minusuygulama doOperation()sağlayabilir. Davranışı yalnızca verilen her tür (alt sınıf) için tanımlanan soyut bir kavram olduğu gibi , sınıfta uygulama doOperation()mantıklı olmaz . Benzer şekilde, belirli bir alt sınıfı, uygulaması olmadan tamamlanmayacaktır . MathSymbolMathSymbolMathSymbolMathSymboldoOperation()

Saf sanal yöntemlerin genellikle kendilerini bildiren sınıfta hiçbir uygulaması olmamasına rağmen, bazı dillerdeki (örneğin C++ ve Python) saf sanal yöntemlerin, türetilmiş bir sınıfın devredebileceği geri dönüş veya varsayılan davranış sağlayarak bildirim sınıflarında bir uygulama içermesine izin verilir. , Eğer uygunsa.

Saf sanal işlevler, bir arabirimi tanımlamak için yöntem bildirimlerinin kullanıldığı yerlerde de kullanılabilir - Java'daki interface anahtar sözcüğünün açıkça belirttiğine benzer. Böyle bir kullanımda, türetilmiş sınıflar tüm uygulamaları sağlayacaktır. Böyle bir tasarım modelinde , arabirim görevi gören soyut sınıf yalnızca saf sanal işlevler içerecek , veri üyeleri veya sıradan yöntemler içermeyecektir . C++'da bu tür tamamen soyut sınıfları arabirimler olarak kullanmak işe yarar çünkü C++ çoklu kalıtımı destekler . Ancak, birçok OOP dili çoklu kalıtımı desteklemediğinden, genellikle ayrı bir arabirim mekanizması sağlarlar. Bir örnek Java programlama dilidir .

İnşaat ve yıkım sırasındaki davranış

Diller , bir nesnenin yapıcısı veya yıkıcısı çalışırken davranışlarında farklılık gösterir . Bu nedenle, yapıcılarda sanal işlevlerin çağrılması genellikle önerilmez.

C++'da "temel" işlev çağrılır. Spesifik olarak, mevcut kurucunun sınıfından daha fazla türetilmemiş en türetilmiş işlev çağrılır. Bu işlev saf bir sanal işlevse, tanımsız davranış oluşur. Bu, sınıf bu saf sanal işlev için bir uygulama içeriyor olsa bile geçerlidir. Derleme zamanında veya bağlantı zamanında saf sanal işlevlere yapılan dolaylı çağrıları algılamak için uyumlu bir C++ uygulaması gerekli değildir (ve genellikle mümkün değildir) . Bazı çalıştırma zamanı sistemleri , çalıştırma zamanında salt sanal bir işleve yapılan bir çağrıyla karşılaşıldığında salt sanal bir işlev çağrısı hatası verir .

Java ve C#'da, türetilmiş uygulama çağrılır, ancak bazı alanlar türetilmiş oluşturucu tarafından henüz başlatılmaz (varsayılan sıfır değerlerine başlatılsalar da). Soyut Fabrika Kalıbı gibi bazı tasarım kalıpları , bu yeteneği destekleyen dillerde bu kullanımı aktif olarak destekler.

Sanal yıkıcılar

Nesne yönelimli diller, nesneler oluşturulduğunda ve yok edildiğinde genellikle bellek ayırmayı ve ayırmayı otomatik olarak yönetir. Bununla birlikte, bazı nesne yönelimli diller, istenirse özel bir yıkıcı yönteminin uygulanmasına izin verir. Söz konusu dil otomatik bellek yönetimini kullanıyorsa, çağrılan özel yıkıcının (bu bağlamda genellikle sonlandırıcı olarak adlandırılır) söz konusu nesne için uygun olduğu kesindir. Örneğin, Animal'ı devralan Wolf türünde bir nesne oluşturulursa ve her ikisinin de özel yıkıcıları varsa, çağrılan kişi Wolf'ta bildirilen nesne olacaktır.

Manuel bellek yönetimi bağlamlarında, özellikle statik gönderme ile ilgili olarak durum daha karmaşık olabilir . Wolf türünde bir nesne oluşturulur, ancak bir Animal işaretçisi tarafından işaret edilirse ve silinen bu Animal işaretçi türüyse, yok edici sanal olmadıkça, çağrılan yıkıcı, Wolf için değil, aslında Animal için tanımlanmış olabilir. . Bu, özellikle, yıkıcılar sanal değilse davranışın yaygın bir programlama hatası kaynağı olduğu C++ için geçerlidir.

Ayrıca bakınız

Referanslar