Saf Sanal Fonksiyonlar
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 59 / 104.
Bir saf sanal fonksiyon (pure virtual function), temel sınıfta herhangi bir gerçeklemesi bulunmayan sanal bir fonksiyondur. Fonksiyon bildirimine = 0 atanarak bildirilir. Bu, derleyiciye türetilmiş sınıfların kendi gerçeklemelerini sağlamasının zorunlu olduğunu söyler.
class Shape {
public:
virtual double area() = 0; // Saf sanal fonksiyon
virtual ~Shape() = default;
};Varsayılan bir davranış sağlayan normal sanal fonksiyonların aksine, saf sanal fonksiyonlar bir sözleşme tanımlar: Herhangi bir somut türetilmiş sınıfın örneklenebilir olması için bu fonksiyonu uygulaması gerekir. Temel sınıf sadece ne yapılması gerektiğini bildirir, nasıl yapılacağını değil.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width * height;
}
};Hem Circle hem de Rectangle, area() fonksiyonunu uygulamalıdır çünkü bu Shape içinde saf sanal (pure virtual) bir fonksiyondur. Eğer türetilmiş bir sınıf tüm saf sanal fonksiyonları uygulayamazsa, o da soyut (abstract) hale gelir ve örneklendirilemez.
Taban sınıfın anlamlı bir varsayılan uygulama sağlayamadığı durumlarda saf sanal fonksiyonlar (pure virtual functions) temeldir. Her şeklin bir alanı vardır, ancak belirli şekil türünü bilmeden "genel şekil alanı" hesaplamanın mantıklı bir yolu yoktur.
Görev
KolaySaf sanal fonksiyonların (pure virtual functions) farklı ödeme yöntemleri arasında bir sözleşmeyi nasıl zorunlu kıldığını gösteren bir ödeme işleme sistemi kuralım. Her ödeme işlemcisinin ne yapması gerektiğini tanımlayan soyut bir temel sınıf oluşturacak, ardından bu sözleşmeyi yerine getiren somut ödeme türlerini uygulayacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
PaymentProcessor.h: Tüm ödeme yöntemleri için bir taslak görevi gören soyut birPaymentProcessorsınıfı tanımlayın. Bu sınıf şunlara sahip olmalıdır:- Bir korumalı (protected)
std::string accountIdüyesi - Hesap kimliğini (account ID) başlatan bir yapıcı (constructor)
- Bir saf sanal (pure virtual)
processPayment(double amount)metodu — her ödeme türü bunu farklı şekilde uygulamalıdır std::stringdöndüren bir saf sanalgetProcessorName()metodu- Bir sanal yıkıcı (virtual destructor)
- Bir korumalı (protected)
PaymentMethods.h:PaymentProcessorsınıfından miras alan iki somut ödeme işlemcisi uygulayın:CreditCardProcessor:- Bir özel (private)
double feePercentageüyesi (işlem ücreti oranı) - Hesap kimliği ve ücret yüzdesini alan bir yapıcı
- Ücreti hesaplamak (amount * feePercentage / 100) ve ardından şunu yazdırmak için
processPayment()metodunu uygulayın:Credit Card [<accountId>]: Charged Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)lt;amount> (Fee: Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)lt;fee>) "CreditCard"döndürmek içingetProcessorName()metodunu uygulayın
BankTransferProcessor:- Bir özel (private)
std::string bankNameüyesi - Hesap kimliği ve banka adını alan bir yapıcı
- Şunu yazdırmak için
processPayment()metodunu uygulayın:Bank Transfer [<accountId>] via <bankName>: Transferred Bank Transfer [<accountId>] via <bankName>: Transferred $<amount>lt;amount> "BankTransfer"döndürmek içingetProcessorName()metodunu uygulayın
- Bir özel (private)
main.cpp: Dört girişi (her biri ayrı bir satırda) okuyun:- Kredi kartı hesap kimliği
- Kredi kartı ücret yüzdesi (double)
- Banka hesap kimliği
- Banka adı
Her iki ödeme işlemcisini de oluşturun ve bunları bir
PaymentProcessor*işaretçi dizisinde saklayın. Her işlemci üzerinden100.0tutarında bir ödeme gerçekleştirin ve her işlemden önce işlemci adını görüntüleyin:Processing with <processorName>: <processPayment output>İşlemciler arasına boş bir satır yazdırın. İşiniz bittiğinde dinamik olarak ayrılmış nesnelerinizi temizleyin.
Örneğin, CC-4521, 2.5, BA-7890 ve National Bank girişleri ile:
Processing with CreditCard:
Credit Card [CC-4521]: Charged $100 (Fee: $2.5)
Processing with BankTransfer:
Bank Transfer [BA-7890] via National Bank: Transferred $100PaymentProcessor saf sanal fonksiyonlara sahip olduğu için, onu doğrudan örnekleyemezsiniz — yalnızca sözleşmeyi yerine getiren somut uygulamalar oluşturulabilir. Bu, her ödeme yönteminin kendi özel işleme mantığını sağlamasını garanti eder.
Kopya kağıdı
Bir saf sanal fonksiyon (pure virtual function), bir sanal fonksiyona = 0 atanarak bildirilir ve bu, temel sınıfta bir uygulaması olmadığını gösterir:
class Shape {
public:
virtual double area() = 0; // Pure virtual function
virtual ~Shape() = default;
};Saf sanal fonksiyonlar, türetilmiş sınıfların mutlaka uygulaması gereken bir sözleşme oluşturur. Temel sınıf, nasıl yapılacağını değil, ne yapılması gerektiğini tanımlar.
Türetilmiş sınıfların örneklenebilir (instantiable) olması için tüm saf sanal fonksiyonları geçersiz kılması (override) gerekir:
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};Eğer türetilmiş bir sınıf tüm saf sanal fonksiyonları uygulamazsa, o da soyut (abstract) hale gelir ve örneklenemez.
Temel sınıfın anlamlı bir varsayılan uygulama sağlayamadığı durumlarda saf sanal fonksiyonları kullanın.
Kendin dene
#include <iostream>
#include <string>
#include "PaymentMethods.h"
using namespace std;
int main() {
// Girdileri oku
string ccAccountId;
double feePercentage;
string bankAccountId;
string bankName;
getline(cin, ccAccountId);
cin >> feePercentage;
cin.ignore();
getline(cin, bankAccountId);
getline(cin, bankName);
// TODO: 2 elemanlı bir PaymentProcessor* işaretçi dizisi oluşturun
// TODO: CreditCardProcessor ve BankTransferProcessor nesneleri oluşturun
// ve bunları diziye kaydedin
// TODO: Dizi üzerinde döngü kurun ve her işlemci için:
// 1. "Processing with <processorName>:" yazdırın
// 2. 100.0 tutarı ile processPayment fonksiyonunu çağırın
// 3. İşlemciler arasında boş bir satır yazdırın (sonuncusundan sonra değil)
// TODO: Dinamik olarak ayrılmış nesneleri temizleyin
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