Menu
Coddy logo textTech

Sanal Fonksiyonlar ve VTable

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 52 / 104.

virtual anahtar sözcüğü, önceki derste gördüğümüz sorunu çözer. Bir yöntemi sanal olarak bildirdiğinizde C++, hangi sürümün çağrılacağını işaretçi türüne göre değil, çalışma zamanındaki gerçek nesne türüne göre belirler.

class Animal {
public:
    virtual void speak() {
        std::cout << "Some sound" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() override {
        std::cout << "Woof!" << std::endl;
    }
};

Dog d;
Animal* ptr = &d;
ptr->speak();  // Çıktı: Woof!

Şimdi doğru yöntem çağrılıyor! override anahtar sözcüğü isteğe bağlıdır ancak önerilir. Derleyiciye sanal bir işlevi geçersiz kılmayı amaçladığını bildirir ve imzalar eşleşmezse hataları yakalar.

Bu nasıl çalışır? Bir sınıfta sanal işlevler olduğunda derleyici bir sanal tablo (vtable) oluşturur. Bu, sınıfın sanal işlevlerine yönelik işaretçileri içeren gizli bir arama tablosudur. Sanal işlevlere sahip her nesne, sınıfının vtable'ına yönelik gizli bir işaretçi (vptr) depolar.

Bir sanal işlevi bir işaretçi üzerinden çağırdığınızda program, çalışma zamanında doğru işlev adresini vtable içinde arar. Buna dinamik gönderim denir. Normal işlev çağrılarına kıyasla küçük bir ek yük getirir, ancak güçlü polimorfik davranış sağlar.

Önemli bir kural: Bir sınıf sanal işlevlere sahipse ve temel sınıf olarak kullanılacaksa, yıkıcısı da sanal olmalıdır. Bu, türetilmiş nesneler temel sınıf işaretçileri üzerinden silinirken uygun temizlemenin yapılmasını sağlar:

class Animal {
public:
    virtual ~Animal() {}  // Sanal yıkıcı
    virtual void speak() {}
};
challenge icon

Görev

Kolay

Sanal işlevlerin ve çalışma zamanı çok biçimliliğinin gücünü gösteren bir medya oynatıcı sistemi oluşturalım. Her birinin farklı şekilde oynatıldığı bir medya türleri hiyerarşisi oluşturacak ve temel sınıf işaretçileri üzerinden erişildiğinde bile sanal işlevlerin doğru davranışı nasıl etkinleştirdiğini göreceksin.

Kodunu üç dosyaya ayıracaksın:

  • Media.h: Oynatılabilir herhangi bir medyayı temsil eden bir temel Media sınıfı tanımla:
    • Korumalı bir std::string title üyesi
    • Bir başlık alan ve bunu saklayan bir kurucu
    • Şunu yazdıran bir sanal play() yöntemi: Playing media: <title>
    • Şunu yazdıran bir sanal yıkıcı: Media [<title>] destroyed
  • AudioTrack.h: AudioTrack sınıfından herkese açık olarak kalıtım alan bir Media sınıfı tanımla:
    • Özel bir std::string artist üyesi
    • Bir başlık ve sanatçı alan, başlığı temel sınıfa aktaran ve sanatçıyı saklayan bir kurucu
    • play() yöntemini override anahtar sözcüğünü kullanarak geçersiz kıl ve şunu yazdır: Playing audio: <title> by <artist>
    • Şunu yazdıran bir yıkıcı: AudioTrack [<title>] destroyed
  • main.cpp: Üç girdi oku (her biri ayrı bir satırda):
    1. Ses parçasının başlığı (string)
    2. Sanatçının adı (string)
    3. Videonun başlığı (string)

    VideoClip sınıfından herkese açık olarak kalıtım alan bir Media sınıfını doğrudan main.cpp içinde tanımla:

    • Özel bir int duration üyesi (saniye cinsinden)
    • Bir başlık ve süre alan bir kurucu (varsayılan süreyi 120 olarak ayarla)
    • play() yöntemini geçersiz kılarak şunu yazdır: Playing video: <title> (<duration>s)
    • Şunu yazdıran bir yıkıcı: VideoClip [<title>] destroyed

    Bir blok kapsamı içinde üç adet Media* işaretçisinden oluşan bir dizi oluştur. Dinamik olarak şunları ayır:

    • Başlığı "Generic Media" olan bir temel Media nesnesi
    • Girdi olarak alınan başlık ve sanatçıyla bir AudioTrack nesnesi
    • Girdi olarak alınan video başlığıyla bir VideoClip nesnesi

    Dizi üzerinde döngü kurarak her işaretçi üzerinde play() çağır. Ardından tüm nesneleri ters sırayla sil. Bloktan sonra şunu yazdır: Playback complete!

Örneğin girdiler Bohemian Rhapsody, Queen ve Nature Documentary olduğunda:

Playing media: Generic Media
Playing audio: Bohemian Rhapsody by Queen
Playing video: Nature Documentary (120s)
VideoClip [Nature Documentary] destroyed
Media [Nature Documentary] destroyed
AudioTrack [Bohemian Rhapsody] destroyed
Media [Bohemian Rhapsody] destroyed
Media [Generic Media] destroyed
Playback complete!

play() işaretçileri üzerinden Media* çağrısının, sanal işlevler sayesinde doğru türetilmiş sınıf yöntemini çağırdığına dikkat et. Ayrıca sanal yıkıcının uygun temizlemeyi nasıl sağladığını gözlemle: bir temel sınıf işaretçisi üzerinden silme yapıldığında hem türetilmiş sınıfın hem de temel sınıfın yıkıcıları çalışır.

Kendin dene

#include <iostream>
#include <string>
#include "Media.h"
#include "AudioTrack.h"

using namespace std;

// TODO: VideoClip sınıfını burada Media'dan public olarak miras alacak şekilde tanımla
// - Private int duration üyesi (saniye cinsinden)
// - Constructor title ve duration alır (varsayılan duration 120)
// - Override play() to print: Playing video: <title> (<duration>s)
// - Destructor prints: VideoClip [<title>] destroyed

class VideoClip : public Media {
private:
    int duration;

public:
    // TODO: Constructor'ı implement et
    VideoClip(const std::string& t, int d = 120) : Media(t) {
        // TODO: duration değerini sakla
    }

    // TODO: play() metodunu override et
    void play() override {
        // TODO: Bu metodu implement et
    }

    // TODO: Destructor'ı implement et
    ~VideoClip() {
        // TODO: Bu destructor'ı implement et
    }
};

int main() {
    // Girdileri oku
    string audioTitle;
    string artist;
    string videoTitle;
    
    getline(cin, audioTitle);
    getline(cin, artist);
    getline(cin, videoTitle);

    // TODO: Süslü parantezlerle bir blok kapsamı oluştur
    // Blok içinde:
    // 1. Üç Media* işaretçisinden oluşan bir dizi oluştur
    // 2. Dinamik olarak ayır:
    //    - A base Media object with title "Generic Media"
    //    - Girilen başlık ve sanatçı ile bir AudioTrack nesnesi
    //    - Girilen video başlığı ile bir VideoClip nesnesi
    // 3. Diziyi döngüyle gez ve her işaretçi üzerinde play() çağır
    // 4. Tüm nesneleri ters sırada sil

    // TODO: After the block, print: Playback complete!
    
    return 0;
}
quiz iconKendini test et

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

Kendi başına pratik yap: Online C++ derleyicisi