runtime polymorphism c
C ++ 'da Çalışma Zamanı Polimorfizminin Ayrıntılı Bir Çalışması.
Çalışma zamanı polimorfizmi, dinamik polimorfizm veya geç bağlanma olarak da bilinir. Çalışma zamanı polimorfizminde, işlev çağrısı çalışma zamanında çözümlenir.
Buna karşılık, zamanı veya statik polimorfizmi derlemek için, derleyici nesneyi çalışma zamanında çıkarır ve ardından nesneye bağlanacak işlev çağrısına karar verir. C ++ 'da, çalışma zamanı polimorfizmi yöntem geçersiz kılma kullanılarak uygulanır.
Bu eğitimde, çalışma zamanı polimorfizmi hakkında her şeyi ayrıntılı olarak inceleyeceğiz.
=> TÜM C ++ Öğreticilerini Buradan Kontrol Edin.
Ne öğreneceksin:
- İşlev Geçersiz Kılma
- Sanal İşlev
- Sanal Masa ve _vptr Çalışması
- Saf Sanal İşlevler ve Soyut Sınıf
- Sanal Yıkıcılar
- Sonuç
- Önerilen Kaynaklar
İşlev Geçersiz Kılma
İşlev geçersiz kılma, temel sınıfta tanımlanan bir işlevin türetilmiş sınıfta bir kez daha tanımlandığı mekanizmadır. Bu durumda, fonksiyonun türetilmiş sınıfta geçersiz kılındığını söylüyoruz.
İşlev geçersiz kılmanın bir sınıf içinde yapılamayacağını hatırlamalıyız. İşlev yalnızca türetilmiş sınıfta geçersiz kılınır. Bu nedenle, işlev geçersiz kılma için kalıtım mevcut olmalıdır.
İkinci şey, geçersiz kıldığımız bir temel sınıftan gelen işlevin aynı imzaya veya prototipe sahip olması, yani aynı ada, aynı dönüş türüne ve aynı argüman listesine sahip olması gerektiğidir.
Yöntemin geçersiz kılındığını gösteren bir örnek görelim.
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< Çıktı:
Sınıf :: Temel
Sınıf :: Türetilmiş
Yukarıdaki programda, bir temel sınıfımız ve türetilmiş bir sınıfımız var. Temel sınıfta, türetilmiş sınıfta geçersiz kılınan bir show_val fonksiyonumuz var. Ana işlevde, Base ve Derived sınıflarından her biri bir nesne oluşturuyoruz ve her nesne ile show_val işlevini çağırıyoruz. İstenilen çıktıyı üretir.
Her sınıfın nesnelerini kullanan işlevlerin yukarıdaki bağlanması, statik bağlamanın bir örneğidir.
Şimdi temel sınıf işaretçisini kullandığımızda ve türetilmiş sınıf nesnelerini içeriği olarak atadığımızda ne olduğunu görelim.
Örnek program aşağıda gösterilmiştir:
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() //overridden function { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //Early Binding }
Çıktı:
Sınıf :: Temel
Şimdi çıktının 'Class :: Base' olduğunu görüyoruz. Bu nedenle, temel işaretçinin hangi tür nesneyi tuttuğuna bakılmaksızın, program, temel gösterici türü olan sınıfın işlevinin içeriğini çıkarır. Bu durumda, statik bağlama da gerçekleştirilir.
Temel işaretçi çıktısını, doğru içerikleri ve doğru bağlamayı yapmak için, fonksiyonların dinamik bağlanmasına gidiyoruz. Bu, sonraki bölümde açıklanan Sanal işlevler mekanizması kullanılarak gerçekleştirilir.
Sanal İşlev
Geçersiz kılınan işlevin dinamik olarak işlev gövdesine bağlanması için, 'virtual' anahtar sözcüğünü kullanarak temel sınıf işlevini sanal hale getiriyoruz. Bu sanal işlev, türetilmiş sınıfta geçersiz kılınan bir işlevdir ve derleyici bu işlev için geç veya dinamik bağlama gerçekleştirir.
Şimdi sanal anahtar kelimeyi içerecek şekilde yukarıdaki programı aşağıdaki gibi değiştirelim:
#include using namespace std;. class Base { public: virtual void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //late Binding }
Çıktı:
Sınıf :: Türetilmiş
Dolayısıyla Base'in yukarıdaki sınıf tanımında, show_val fonksiyonunu “sanal” olarak yaptık. Temel sınıf işlevi sanal hale getirildiğinde, türetilmiş sınıf nesnesini temel sınıf işaretçisine atadığımızda ve show_val işlevini çağırdığımızda, bağlama çalışma zamanında gerçekleşir.
Böylece, temel sınıf işaretçisi türetilmiş sınıf nesnesi içerdiğinden, türetilmiş sınıftaki show_val işlev gövdesi, show_val işlevine ve dolayısıyla çıktıya bağlıdır.
C ++ 'da, türetilmiş sınıftaki geçersiz kılınan işlev de özel olabilir. Derleyici, yalnızca nesnenin türünü derleme zamanında kontrol eder ve işlevi çalışma zamanında bağlar, dolayısıyla işlev genel veya özel olsa bile herhangi bir fark yaratmaz.
Bir işlev temel sınıfta sanal olarak bildirilirse, tüm türetilmiş sınıflarda sanal olacağını unutmayın.
bir xml dosyasını nasıl okurum
Ancak şimdiye kadar, bağlanacak doğru işlevi tanımlamada tam olarak sanal işlevlerin nasıl bir rol oynadığını veya başka bir deyişle, bağlanmanın gerçekte ne kadar geç gerçekleştiğini tartışmadık.
Sanal işlev, çalışma zamanında işlev gövdesine doğru bir şekilde bağlanır. sanal tablo (VTABLE) ve adında gizli bir işaretçi _vptr.
Bu kavramların ikisi de dahili uygulamalardır ve doğrudan program tarafından kullanılamaz.
Sanal Masa ve _vptr Çalışması
İlk önce sanal tablonun (VTABLE) ne olduğunu anlayalım.
Derleme zamanında derleyici, sanal işlevlere sahip bir sınıfın yanı sıra sanal işlevlere sahip sınıflardan türetilen sınıflar için birer VTABLE ayarlar.
VTABLE, sınıfın nesneleri tarafından çağrılabilen sanal işlevlere işlev işaretçileri olan girdileri içerir. Her sanal işlev için bir işlev işaretçisi girişi vardır.
Saf sanal işlevler söz konusu olduğunda bu giriş NULL olur. (Soyut sınıfı somutlaştıramamamızın nedeni budur).
Vtable işaretçisi olarak adlandırılan sonraki varlık, _vptr, derleyicinin temel sınıfa eklediği gizli bir işaretleyicidir. Bu _vptr, sınıfın vtable'ına işaret eder. Bu temel sınıftan türetilen tüm sınıflar _vptr'yi miras alır.
Sanal işlevleri içeren bir sınıfın her nesnesi bu _vptr'yi dahili olarak depolar ve kullanıcıya şeffaftır. Bir nesnenin kullanıldığı her sanal işlev çağrısı, bu _vptr kullanılarak çözümlenir.
Vtable ve _vtr'nin çalışmasını göstermek için bir örnek alalım.
#include using namespace std; class Base_virtual { public: virtual void function1_virtual() {cout<<'Base :: function1_virtual()
';}; virtual void function2_virtual() {cout<<'Base :: function2_virtual()
';}; virtual ~Base_virtual(){}; }; class Derived1_virtual: public Base_virtual { public: ~Derived1_virtual(){}; virtual void function1_virtual() { coutfunction2_virtual(); delete (b); return (0); }
Çıktı:
Derived1_virtual :: function1_virtual ()
Base :: function2_virtual ()
Yukarıdaki programda, iki sanal işleve ve bir sanal yıkıcıya sahip bir temel sınıfımız var. Ayrıca temel sınıftan bir sınıf türettik ve bunun içinde; sadece bir sanal işlevi geçersiz kıldık. Ana işlevde, türetilmiş sınıf işaretçisi temel işaretçiye atanır.
Ardından, bir temel sınıf işaretçisi kullanarak her iki sanal işlevi çağırırız. Geçersiz kılınan işlevin temel işlev değil çağrıldığında çağrıldığını görüyoruz. İkinci durumda ise, işlev geçersiz kılınmadığından, temel sınıf işlevi çağrılır.
Şimdi yukarıdaki programın vtable ve _vptr kullanılarak dahili olarak nasıl temsil edildiğini görelim.
Önceki açıklamaya göre, sanal işlevlere sahip iki sınıf olduğundan, her sınıf için bir tane olmak üzere iki vtable'ımız olacak. Ayrıca, temel sınıf için _vptr mevcut olacaktır.
Yukarıda gösterilen, yukarıdaki program için vtable düzeninin nasıl olacağının resimli temsilidir. Temel sınıf için vtable basittir. Türetilmiş sınıf durumunda, sadece function1_virtual yazılır.
Dolayısıyla, türetilmiş sınıf vtable'da, işlev1_virtual için işlev göstericisinin türetilmiş sınıftaki geçersiz kılınan işlevi gösterdiğini görüyoruz. Öte yandan, işlev2_virtual için işlev işaretçisi temel sınıftaki bir işlevi gösterir.
Bu nedenle, yukarıdaki programda temel işaretçi türetilmiş bir sınıf nesnesine atandığında, temel işaretçi türetilmiş sınıfın _vptr'sini gösterir.
Yani b-> function1_virtual () çağrısı yapıldığında, türetilmiş sınıftan function1_virtual çağrılır ve b-> function2_virtual () işlev çağrısı yapıldığında, bu işlev işaretçisi temel sınıf işlevini gösterdiğinden, temel sınıf işlevi denir.
Saf Sanal İşlevler ve Soyut Sınıf
Bir önceki bölümümüzde C ++ 'da sanal fonksiyonlar ile ilgili detayları görmüştük. C ++ 'da ayrıca bir ' saf sanal işlev 'Bu genellikle sıfıra eşittir.
Saf sanal işlev aşağıda gösterildiği gibi bildirilmiştir.
virtual return_type function_name(arg list) = 0;
En az bir saf sanal işleve sahip olan sınıf ' soyut sınıf ”. Soyut sınıfı asla somutlaştıramayız, yani soyut sınıfın bir nesnesini yaratamayız.
Bunun nedeni, VTABLE'daki (sanal tablo) her sanal işlev için bir giriş yapıldığını bilmemizdir. Ancak saf bir sanal işlev olması durumunda, bu girişin herhangi bir adresi yoktur ve bu nedenle eksik kalır. Dolayısıyla derleyici, tamamlanmamış VTABLE girişi olan sınıf için bir nesne oluşturmaya izin vermez.
Soyut bir sınıfı örnekleyemememizin nedeni budur.
Aşağıdaki örnek, Soyut sınıfın yanı sıra Saf sanal işlevi gösterecektir.
#include using namespace std; class Base_abstract { public: virtual void print() = 0; // Pure Virtual Function }; class Derived_class:public Base_abstract { public: void print() { cout <<'Overriding pure virtual function in derived class
'; } }; int main() { // Base obj; //Compile Time Error Base_abstract *b; Derived_class d; b = &d; b->print(); }
Çıktı:
Türetilmiş sınıfta saf sanal işlevi geçersiz kılma
Yukarıdaki programda, onu soyut bir sınıf yapan saf bir sanal fonksiyon içeren Base_abstract olarak tanımlanan bir sınıfa sahibiz. Sonra Base_abstract'tan bir 'Derived_class' sınıfı türetiyoruz ve içindeki saf sanal fonksiyon baskısını geçersiz kılıyoruz.
Ana işlevde, ilk satır yorumlanmaz. Bunun nedeni, eğer onun açıklamasını kaldırırsak, soyut bir sınıf için bir nesne oluşturamadığımız için derleyicinin bir hata vermesidir.
Ancak ikinci satırdan itibaren kod çalışır. Başarıyla bir temel sınıf işaretçisi oluşturabiliriz ve sonra ona türetilmiş sınıf nesnesi atayabiliriz. Daha sonra, türetilmiş sınıfta geçersiz kılınan yazdırma işlevinin içeriğini çıkaran bir yazdırma işlevi çağırıyoruz.
Soyut sınıfın bazı özelliklerini kısaca listeleyelim:
- Soyut bir sınıfı başlatamayız.
- Soyut bir sınıf, en az bir saf sanal işlev içerir.
- Soyut sınıfı başlatamasak da, bu sınıfa her zaman işaretçiler veya referanslar oluşturabiliriz.
- Soyut bir sınıf, özellikler ve yöntemler gibi bazı uygulamalara ve saf sanal işlevlere sahip olabilir.
- Soyut sınıftan bir sınıf türettiğimizde, türetilen sınıf, soyut sınıftaki tüm saf sanal işlevleri geçersiz kılmalıdır. Bunu yapamazsa, türetilmiş sınıf da soyut bir sınıf olacaktır.
Sanal Yıkıcılar
Sınıfın yıkıcıları sanal olarak ilan edilebilir. Yukarıya yayın yaptığımızda, yani türetilmiş sınıf nesnesini bir temel sınıf işaretçisine atadığımızda, sıradan yıkıcılar kabul edilemez sonuçlar üretebilir.
Örneğin,Sıradan yıkıcının aşağıdaki planını düşünün.
#include using namespace std; class Base { public: ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Çıktı:
Temel Sınıf :: Yıkıcı
Yukarıdaki programda, temel sınıftan miras alınmış bir türetilmiş sınıfa sahibiz. Esas olarak, türetilmiş sınıfın bir nesnesini bir temel sınıf işaretçisine atarız.
İdeal olarak, 'delete b' çağrıldığında çağrılan yıkıcı türetilmiş sınıfa ait olmalıdır, ancak çıktıdan temel sınıfın yıkıcısının temel sınıf işaretçisi olarak adlandırıldığını görebiliriz.
Bundan dolayı, türetilmiş sınıf yıkıcı çağrılmaz ve türetilen sınıf nesnesi bozulmadan kalır, böylece bir bellek sızıntısına neden olur. Bunun çözümü, temel sınıf kurucusunu sanal yapmaktır, böylece nesne işaretçisi doğru yıkıcıyı işaret eder ve nesnelerin uygun şekilde yok edilmesi gerçekleştirilir.
Sanal yıkıcının kullanımı aşağıdaki örnekte gösterilmektedir.
#include using namespace std; class Base { public: virtual ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
Çıktı:
Türetilmiş sınıf :: Destructor
Temel Sınıf :: Yıkıcı
Bu, temel sınıf yıkıcının önüne sanal bir anahtar sözcük eklememiz dışında önceki programla aynı programdır. Temel sınıf yıkıcıyı sanal hale getirerek istenen çıktıyı elde ettik.
Türetilmiş sınıf nesnesini temel sınıf işaretçisine atadığımızda ve ardından temel sınıf işaretçisini sildiğimizde, yıkıcıların nesne oluşturma sırasının tersi sırayla çağrıldığını görebiliriz. Bu, önce türetilmiş sınıf yıkıcının çağrıldığı ve nesnenin yok edildiği ve ardından temel sınıf nesnesinin yok edildiği anlamına gelir.
Not: C ++ 'da, oluşturucular nesnelerin oluşturulmasına ve başlatılmasına dahil olduğundan, yapıcılar hiçbir zaman sanal olamaz. Bu nedenle, tüm kurucuların tamamen yürütülmesine ihtiyacımız var.
Sonuç
Çalışma zamanı polimorfizmi, yöntemi geçersiz kılma kullanılarak uygulanır. Yöntemleri ilgili nesneleriyle çağırdığımızda bu iyi çalışır. Ancak bir temel sınıf göstericimiz olduğunda ve türetilmiş sınıf nesnelerine işaret eden temel sınıf işaretçisini kullanarak geçersiz kılınan yöntemleri çağırdığımızda, statik bağlantı nedeniyle beklenmeyen sonuçlar ortaya çıkar.
Bunun üstesinden gelmek için sanal işlevler kavramını kullanıyoruz. Vtables ve _vptr'nin dahili temsiliyle, sanal işlevler istenen işlevleri doğru bir şekilde çağırmamıza yardımcı olur. Bu eğiticide, C ++ 'da kullanılan çalışma zamanı polimorfizmini ayrıntılı olarak gördük.
Bununla birlikte, C ++ 'da nesne yönelimli programlama hakkındaki eğitimlerimizi sonlandırıyoruz. Bu öğreticinin, C ++ 'da nesne yönelimli programlama kavramlarını daha iyi ve kapsamlı bir şekilde anlamaya yardımcı olacağını umuyoruz.
=> Sıfırdan C ++ Öğrenmek İçin Burayı Ziyaret Edin.
Önerilen Kaynaklar
- C ++ 'da Polimorfizm
- C ++ 'da Kalıtım
- C ++ 'da Arkadaş İşlevleri
- C ++ 'da Sınıflar ve Nesneler
- Bir Web Sayfasındaki Açılır Öğeleri Kullanmak için Selenium Select Sınıfı Kullanımı - Selenium Eğitimi # 13
- Uygulamalı Örneklerle Python Ana İşlev Eğitimi
- Java Sanal Makinesi: JVM, Java Uygulamasını Çalıştırmada Nasıl Yardımcı Olur?
- LoadRunner VuGen Komut Dosyası Dosyaları ve Çalışma Zamanı Ayarları Nasıl Kurulur