Menu
Coddy logo textTech

Sanal Fonksiyonlara Yeniden Bakış

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

Artık derleme zamanı ve çalışma zamanı çok biçimliliği arasındaki farkı anladığımıza göre, sanal fonksiyonlara ve onları kullanmayı daha güvenli hâle getiren override anahtar sözcüğüne daha yakından bakalım.

Bir işlevi virtual olarak base sınıfta işaretlediğinizde, türetilmiş sınıflar kendi uygulamalarını sağlayabilir. override belirteci, derleyiciye bir sanal işlevi geçersiz kılmayı amaçladığınızı açıkça bildirir:

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

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

override kullanmak çok önemlidir, çünkü hataları derleme zamanında yakalar. İşlev adını yanlışlıkla yazım hatasıyla yazarsanız veya yanlış parametreler kullanırsanız, derleyici sessizce yeni bir işlev oluşturmak yerine sizi uyarır:

class Cat : public Animal {
public:
    void speek() override {  // Derleyici hatası: override edilecek fonksiyon yok
        std::cout << "Meow!" << std::endl;
    }
};

final belirteci, daha fazla geçersiz kılmayı engeller. Türetilmiş sınıfın belirli bir function için geçersiz kılma işlemi yapacak son sınıf olması gerektiğinde bunu kullanın:

class Bulldog : public Dog {
public:
    void speak() override final {
        std::cout << "Gruff woof!" << std::endl;
    }
};

class TinyBulldog : public Bulldog {
    void speak() override {}  // Hata: final fonksiyon override edilemez
};

Sanal işlevleri geçersiz kılarken her zaman override kullanın. Bu, amacınızı belgelendirir ve derleyicinin gerçekten mevcut bir sanal işlevi geçersiz kıldığınızı doğrulamasını sağlar.

challenge icon

Görev

Kolay

Sanal işlevlerle birlikte override ve final anahtar sözcüklerinin gücünü gösteren bir bildirim sistemi oluşturalım. Bazı yöntemlerin daha fazla özelleştirilebildiği, bazılarının ise değiştirilmelerini önlemek için kilitlendiği bir bildirim işleyicileri hiyerarşisi oluşturacaksın.

Kodunu üç dosya arasında düzenleyeceksin:

  • Notifier.h: Herhangi bir bildirim göndericisini temsil eden temel bir Notifier sınıfı tanımla:
    • Korumalı bir std::string recipient üyesi
    • Alıcı adını alan ve saklayan bir kurucu
    • Şunu yazdıran sanal bir send(const std::string& message) yöntemi: Notifying <recipient>: <message>
    • getType() dizesini döndüren sanal bir "Generic" yöntemi
    • Sanal bir yok edici
  • EmailNotifier.h: EmailNotifier sınıfından kalıtım alan bir Notifier sınıfı tanımla:
    • Özel bir std::string domain üyesi
    • Alıcıyı ve alan adını alan, alıcıyı temel sınıfa aktaran bir kurucu
    • Şunu yazdıracak şekilde send() yöntemini geçersiz kıl: Emailing <recipient>@<domain>: <message>
    • getType() yöntemini geçersiz kıl ve final olarak işaretle. "Email" döndürmelidir

    Ardından UrgentEmailNotifier sınıfından kalıtım alan bir EmailNotifier sınıfı tanımla:

    • Alıcıyı ve alan adını alan, her ikisini de EmailNotifier sınıfına aktaran bir kurucu
    • Şunu yazdıracak şekilde send() yöntemini geçersiz kıl: [URGENT] Emailing <recipient>@<domain>: <message>
    • Not: getType() içinde final olarak işaretlendiği için burada EmailNotifier yöntemini geçersiz kılamazsın
  • main.cpp: Üç girdi oku (her biri ayrı bir satırda):
    1. Alıcı adı
    2. E-posta alan adı
    3. Mesaj metni

    Dinamik olarak üç bildirim nesnesi oluştur: temel bir Notifier, bir EmailNotifier ve bir UrgentEmailNotifier; tümünde aynı alıcıyı (uygun olanlarda aynı alan adını) kullan. Bunları Notifier* işaretçilerinden oluşan bir dizide sakla.

    Dizi üzerinde döngü kur ve her bildirim için önce getType() kullanarak türünü yazdır, ardından mesajınla birlikte send() yöntemini çağır. Her girdiyi şu biçimde düzenle:

    Type: <type>
    <send output>

    Her bildirim arasında boş bir satır yazdır. İşin bittiğinde dinamik olarak ayrılmış nesneleri temizle.

Örneğin girdiler Alice, company.com ve Meeting at 3pm olduğunda:

Type: Generic
Notifying Alice: Meeting at 3pm

Type: Email
Emailing Alice@company.com: Meeting at 3pm

Type: Email
[URGENT] Emailing Alice@company.com: Meeting at 3pm

UrgentEmailNotifier sınıfının mesaj biçimini özelleştirmek için send() yöntemini nasıl geçersiz kılabildiğine, ancak "Email" yöntemi EmailNotifier olarak işaretlendiği için getType() sınıfından final türünü devraldığına dikkat et. Derleme zamanında imza uyuşmazlıklarını yakalamak için geçersiz kılınan tüm yöntemlerde override anahtar sözcüğünü kullan.

Kendin dene

#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"

using namespace std;

int main() {
    // Girdileri oku
    string recipient;
    string domain;
    string message;
    
    getline(cin, recipient);
    getline(cin, domain);
    getline(cin, message);
    
    // TODO: 3 elemanlı Notifier* işaretçi dizisi oluştur
    
    // TODO: Dinamik olarak oluştur:
    // - recipient ile temel bir Notifier
    // - recipient ve domain ile bir EmailNotifier
    // - recipient ve domain ile bir UrgentEmailNotifier
    
    // TODO: Diziyi döngüyle gez ve her notifier için:
    // - "Type: " yazdır, ardından getType() sonucunu
    // - message ile send() çağır
    // - notifier'lar arasında boş bir satır yazdır (sonuncudan sonra değil)
    
    // TODO: Dinamik olarak ayrılmış nesneleri temizle
    
    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