Metot Geçersiz Kılma
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 51 / 104.
Metot geçersiz kılma (Method overriding), türetilmiş bir sınıfın, temel sınıfta zaten mevcut olan bir metodun kendi uygulamasını sağlamasıyla gerçekleşir. Bu, türetilmiş sınıfların aynı metot imzasını korurken kalıtımla alınan davranışı özelleştirmesine olanak tanır.
Bir metodu geçersiz kılmak (override etmek) için, türetilmiş sınıfta tam olarak aynı isim, dönüş türü ve parametrelerle bir metot tanımlamanız yeterlidir:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Çıktı: Woof!Türetilmiş sınıf versiyonu temel sınıf versiyonunu gizler. Ancak, kapsam çözünürlük operatörünü kullanarak temel sınıf metoduna hala erişebilirsiniz:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Temel sınıf versiyonunu çağır
std::cout << "Woof!" << std::endl;
}
};Bu temel geçersiz kılma (overriding) biçiminde önemli bir sınırlama vardır. Türetilmiş bir nesneye bir temel sınıf işaretçisi veya referansı aracılığıyla eriştiğinizde, bunun yerine temel sınıf sürümü çağrılır:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Çıktı: Some sound (not Woof!)Bu durum, derleyicinin hangi metodun çağrılacağını gerçek nesne türüne göre değil, işaretçi türüne göre belirlemesi nedeniyle gerçekleşir. Doğru metodun gerçek nesneye dayalı olarak çağrıldığı gerçek çalışma zamanı polimorfizmini elde etmek için, bir sonraki derste ele alacağımız sanal fonksiyonlara ihtiyacınız olacak.
Görev
KolayMetot geçersiz kılma (method overriding) işlemini uygulamalı olarak gösteren bir bildirim sistemi oluşturalım. Bir temel Notifier sınıfı ve mesajların nasıl iletileceğini özelleştiren iki uzmanlaşmış bildirimci oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
Notifier.h: Aşağıdaki özelliklere sahip bir temelNotifiersınıfı tanımlayın:- Bir korumalı (protected)
std::string recipientüyesi - Bir alıcı adı alan ve bunu saklayan bir yapıcı (constructor)
- Şunu yazdıran bir genel (public)
send(const std::string& message)metodu:Sending to <recipient>: <message>
- Bir korumalı (protected)
EmailNotifier.h:Notifiersınıfından genel (public) olarak türetilen birEmailNotifiersınıfı tanımlayın:- Bir alıcı alan ve bunu temel sınıfa ileten bir yapıcı
send()metodunu, önceNotifier::send()kullanarak temel sınıf versiyonunu çağıracak, ardından şunu yazdıracak şekilde geçersiz kılın:[Email sent successfully]
main.cpp: İki girdi okuyun (her biri ayrı bir satırda):- Alıcı adı (string)
- Mesaj içeriği (string)
Doğrudan main.cpp içinde,
Notifiersınıfından genel (public) olarak türetilen birSMSNotifiersınıfı tanımlayın:- Bir alıcı alan ve bunu temel sınıfa ileten bir yapıcı
send()metodunu şunu yazdıracak şekilde geçersiz kılın:SMS to <recipient>: <message>(temel sınıf davranışını çağırmadan tamamen değiştirerek)
Aynı alıcıyı kullanarak her bildirimci türünden (temel
Notifier,EmailNotifierveSMSNotifier) birer nesne oluşturun. Her biri üzerinde girdi mesajıylasend()metodunu çağırın ve aralarına bir ayırıcı satır yazdırın:--- Base Notifier --- <output> --- Email Notifier --- <output> --- SMS Notifier --- <output>
Örneğin, Alice ve Hello there! girdileriyle:
--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!EmailNotifier sınıfının önce üst metodu çağırarak temel davranışı nasıl genişlettiğine, SMSNotifier sınıfının ise bunu kendi uygulamasıyla nasıl tamamen değiştirdiğine dikkat edin. Başlık korumalarını (header guards) ve her dosyaya uygun başlıkları (headers) eklemeyi unutmayın.
Kopya kağıdı
Metot geçersiz kılma (method overriding), türetilmiş bir sınıfın, temel sınıftaki bir metodun kendi uygulamasını sağlamasına olanak tanır ve aynı metot imzasını (ad, dönüş türü ve parametreler) korur.
Temel geçersiz kılma sözdizimi:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Çıktı: Woof!Türetilmiş sınıf metodu, temel sınıf versiyonunu gizler. Türetilmiş sınıftan temel sınıf metodunu çağırmak için kapsam çözünürlük operatörünü (scope resolution operator) kullanın:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Temel sınıf versiyonunu çağır
std::cout << "Woof!" << std::endl;
}
};Önemli kısıtlama: Türetilmiş bir nesneye bir temel sınıf işaretçisi (pointer) veya referansı aracılığıyla erişildiğinde, temel sınıf versiyonu çağrılır:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Çıktı: Some sound (Woof! değil)Bunun nedeni, derleyicinin hangi metodun çağrılacağını gerçek nesne türüne göre değil, işaretçi türüne göre belirlemesidir. Gerçek çalışma zamanı polimorfizmi (runtime polymorphism) için sanal fonksiyonları (virtual functions) kullanın.
Kendin dene
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
// TODO: Burada Notifier sınıfından genel (public) olarak miras alan SMSNotifier sınıfını tanımlayın
// - Alıcıyı (recipient) temel sınıfa (base class) ileten kurucu metot (constructor)
// - Şunu yazdırmak için send() metodunu geçersiz kılın (override): SMS to <recipient>: <message>
// (temel davranışı tamamen değiştirin, üst sınıfın send() metodunu çağırmayın)
int main() {
string recipient;
string message;
getline(cin, recipient);
getline(cin, message);
// TODO: recipient ile Notifier nesnesi oluşturun
// "--- Base Notifier ---" yazdırın
// message ile send() metodunu çağırın
// TODO: recipient ile EmailNotifier nesnesi oluşturun
// "--- Email Notifier ---" yazdırın
// message ile send() metodunu çağırın
// TODO: recipient ile SMSNotifier nesnesi oluşturun
// "--- SMS Notifier ---" yazdırın
// message ile send() metodunu çağırın
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 Konteyner