Derleme ve Çalışma Zamanı Çok Biçimliliği
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 56 / 104.
Çok biçimlilik, "çok biçim" anlamına gelir ve nesnelerin farklı davranırken tek tipmiş gibi ele alınmasını sağlayan temel bir OOP kavramıdır. C++, her biri program yürütmesinin farklı bir aşamasında çözümlenen iki farklı çok biçimlilik türünü destekler.
Derleme zamanı polimorfizmi (statik polimorfizm olarak da adlandırılır), program çalışmadan önce derleyici tarafından çözümlenir. Derleyici, fonksiyon imzasına göre hangi fonksiyonun çağrılacağını tam olarak belirler. Buna fonksiyon aşırı yükleme ve şablonlar dahildir:
void print(int x) { std::cout << "Integer: " << x << std::endl; }
void print(double x) { std::cout << "Double: " << x << std::endl; }
print(5); // Derleyici print(int) seçer
print(3.14); // Derleyici print(double) seçerÇalışma zamanı çok biçimliliği (dinamik çok biçimlilik olarak da adlandırılır), program çalışırken çözümlenir. Hangi fonksiyonun çağrılacağına ilişkin karar, işaretçi veya başvuru türüne değil, gerçek nesne türüne bağlıdır. Bu, sanal fonksiyonlar aracılığıyla gerçekleştirilir:
class Shape {
public:
virtual void draw() { std::cout << "Drawing shape" << std::endl; }
};
class Circle : public Shape {
public:
void draw() override { std::cout << "Drawing circle" << std::endl; }
};
Shape* s = new Circle();
s->draw(); // Çalışma zamanında karar verildi: "Drawing circle"Temel ödünleşim şudur: derleme zamanı çok biçimliliği, kararlar derleme sırasında verildiği için çalışma zamanı ek yükü oluşturmazken, çalışma zamanı çok biçimliliği küçük bir maliyet ekler (vtable araması) ancak türleri yürütme zamanına kadar bilinmeyen nesnelerle çalışırken daha fazla esneklik sağlar.
Görev
KolayHer iki çok biçimlilik türünü yan yana gösteren bir hesap makinesi sistemi oluşturalım. Derleme zamanı çok biçimliliğinin farklı girdi türlerini işlev aşırı yükleme yoluyla ele aldığı, çalışma zamanı çok biçimliliğinin ise farklı hesaplama stratejilerinin dinamik olarak değiştirilmesine olanak tanıdığı bir sistem oluşturacaksın.
Kodunu üç dosyaya ayıracaksın:
Calculator.h: Herhangi bir hesaplama stratejisini temsil eden temel birCalculatorsınıfı tanımla:calculate(int a, int b)döndüren veintyazdıran (0 döndüren) sanal birBase calculation: <a> ? <b>metodu- Sanal bir yıkıcı
Operations.h: Hesaplama davranışını geçersiz kılan, türetilmiş iki hesap makinesi sınıfı tanımla:Adder:calculate()metodunu geçersiz kılarakAdding: <a> + <b>yazdır ve toplamı döndürMultiplier:calculate()metodunu geçersiz kılarakMultiplying: <a> * <b>yazdır ve çarpımı döndür
overrideanahtar sözcüğünü kullanmalıdır.main.cpp: Her iki çok biçimlilik türünü sergileyen bir sistem oluştur. İki tamsayı girdisi oku (her biri ayrı bir satırda).İlk olarak, üç aşırı yüklenmiş
display()işlevi oluşturarak derleme zamanı çok biçimliliğini göster:display(int x)şunu yazdırır:Integer value: <x>display(double x)şunu yazdırır:Double value: <x>display(const std::string& x)şunu yazdırır:String value: <x>
Ardından, temel bir
Calculator*, birCalculatorve birAdderiçerenMultiplierişaretçilerinden oluşan bir dizi oluşturarak çalışma zamanı çok biçimliliğini göster. Döngüyle her birinin üzerindecalculate()metodunu girdi değerlerinle çağır ve her hesaplamadan sonra sonucu yazdır.Çıktını aşağıdaki gibi yapılandır:
=== Compile-Time Polymorphism === <display outputs for int, double, string> === Runtime Polymorphism === <calculate outputs with results>Derleme zamanı bölümü için
display()metodunu önce ilk girdiyi tamsayı olarak, ardından double olarak (aynı değere .5 eklenmiş hâliyle), sonra da "Result" dizesi olarak çağır. İşin bittiğinde dinamik olarak ayrılmış hesap makinelerinin belleğini temizle.
Örneğin, girdiler 10 ve 3 olduğunda:
=== Compile-Time Polymorphism ===
Integer value: 10
Double value: 10.5
String value: Result
=== Runtime Polymorphism ===
Base calculation: 10 ? 3
Result: 0
Adding: 10 + 3
Result: 13
Multiplying: 10 * 3
Result: 30Derleyicinin, bağımsız değişken türüne göre doğru display() aşırı yüklemesini (derleme zamanı kararı) seçtiğine, doğru calculate() metodunun ise vtable mekanizması aracılığıyla çalışma zamanında gerçek nesne türü tarafından belirlendiğine dikkat et.
Kendin dene
#include <iostream>
#include <string>
#include "Calculator.h"
#include "Operations.h"
// TODO: Üç aşırı yüklenmiş display() fonksiyonu oluşturun:
// 1. display(int x) - prints "Integer value: <x>"
// 2. display(double x) - "Double value: <x>" yazdırır
// 3. display(const std::string& x) - "String value: <x>" yazdırır
int main() {
// İki tamsayı girdisi oku
int a, b;
std::cin >> a;
std::cin >> b;
// === Compile-Time Polymorphism ===
std::cout << "=== Compile-Time Polymorphism ===" << std::endl;
// TODO: display() fonksiyonunu şunlarla çağırın:
// - a bir tamsayı olarak
// - a bir double olarak (0.5 ekleyin)
// - "Result" dizesi
std::cout << std::endl;
// === Runtime Polymorphism ===
std::cout << "=== Runtime Polymorphism ===" << std::endl;
// TODO: 3 elemanlı Calculator* işaretçilerinden bir dizi oluşturun:
// - a base Calculator
// - bir Adder
// - bir Multiplier
// TODO: Diziyi döngüyle dolaşın, her birinde calculate(a, b) çağırın,
// ve her hesaplamadan sonra "Result: <return_value>" yazdır
// TODO: Dinamik olarak ayrılmış belleği temizle
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic KonteynerKendi başına pratik yap: Online C++ derleyicisi