Yükleniyor...

C++ Kalıtım (Inheritance) Temelleri

C++’ta kalıtımın temellerini öğrenin. Taban ve türetilmiş sınıflar, erişim belirleyicileri ve kod tekrarını azaltma örneklerle anlatılıyor.

Kalıtım (inheritance), bir sınıfın başka bir sınıfın özelliklerini ve davranışlarını devralmasını sağlar. Böylece ortak kod tekrarı azalır, sınıflar hiyerarşik olarak düzenlenebilir ve polimorfizm (çok biçimlilik) mümkün olur. Bu makalede bir Book taban sınıfı ile EBook ve PrintedBook türetilmiş sınıflarını, constructor zinciri, virtual fonksiyonlar, override, virtual destructor, upcasting/downcasting ve abstract (soyut) taban sınıf konularıyla birlikte inceleyeceğiz.


1. Taban Sınıf: Book

Ortak alan ve davranışları Book sınıfında topluyoruz. Alt sınıflar bunları devralır ve gerekirse özelleştirir.


#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

class Book {
protected: // alt sınıfların erişebilmesi için protected kullanıyoruz
    string title;
    string author;
    double basePrice;
    int pageCount;

public:
    Book(string t, string a, double price, int pages)
        : title(t), author(a), basePrice(price), pageCount(pages) {
        // Ortak kurulum
    }

    // Sanal yıkıcı: taban pointer üzerinden türetilmiş nesneler yok edilirken şarttır
    virtual ~Book() = default;

    // Sanal metot: Alt sınıflar fiyat hesabını özelleştirebilir
    virtual double GetFinalPrice() const {
        return basePrice;
    }

    // Sanal metot: alt sınıflar ek bilgiyi yazdırabilir
    virtual void PrintInfo() const {
        cout << "[Book] " << title
             << " | Author: " << author
             << " | Pages: " << pageCount
             << " | Price: " << GetFinalPrice() << " TL" << endl;
    }
};

protected üyeler sınıf dışına kapalıdır ancak alt sınıflar tarafından erişilebilir. virtual destructor polimorfik silme için zorunludur.


2. Türetilmiş Sınıf: EBook

E-kitaplarda baskı maliyeti yoktur; fakat KDV, indirim veya lisans bedeli gibi kalemler olabilir. Fiyatlandırmayı özelleştirelim.


class EBook : public Book {
    double vatRate;      // KDV (ör. 0.10 = %10)
    bool drmProtected;   // Dijital hak yönetimi

public:
    EBook(string t, string a, double price, int pages,
          double vat, bool drm)
        : Book(t, a, price, pages), vatRate(vat), drmProtected(drm) {}

    double GetFinalPrice() const override {
        double priceWithVat = basePrice * (1.0 + vatRate);
        // E-kitaplara örnek bir kampanya uygulayalım: sayfa 500+ ise %5 indirim
        if (pageCount >= 500) priceWithVat *= 0.95;
        return priceWithVat;
    }

    void PrintInfo() const override {
        cout << "[EBook] " << title
             << " | DRM: " << (drmProtected ? "Yes" : "No")
             << " | Final Price: " << GetFinalPrice() << " TL" << endl;
    }
};

override kullanmak, imza uyuşmazlıklarını derleme zamanında yakalayarak güvenlik sağlar.


3. Türetilmiş Sınıf: PrintedBook

Basılı kitaplarda kağıt ve kargo maliyetleri olabilir. Kapak türüne göre (ciltsiz/ciltli) farklı çarpanlar uygulayalım.


enum class Cover { Soft, Hard };

class PrintedBook : public Book {
    Cover coverType;
    double shipping;   // kargo ücreti
    double printCost;  // sayfa başına baskı maliyeti

public:
    PrintedBook(string t, string a, double price, int pages,
                Cover cover, double shippingCost, double printPerPage)
        : Book(t, a, price, pages),
          coverType(cover), shipping(shippingCost), printCost(printPerPage) {}

    double GetFinalPrice() const override {
        double cost = basePrice + shipping + (printCost * pageCount);
        if (coverType == Cover::Hard) {
            cost *= 1.10; // ciltli kapak %10 ek
        }
        return cost;
    }

    void PrintInfo() const override {
        cout << "[PrintedBook] " << title
             << " | Cover: " << (coverType == Cover::Hard ? "Hard" : "Soft")
             << " | Final Price: " << GetFinalPrice() << " TL" << endl;
    }
};

Constructor zinciri ilk olarak taban sınıfın kurucusunu (Book) çağırır, ardından PrintedBook özel alanlarını başlatır.


4. Polimorfizm: Upcasting ve Sanal Çağrılar

Türetilmiş nesneleri taban tür pointer veya referansla tutup sanal fonksiyonları çağırabiliriz (dinamik polimorfizm).


int main() {
    vector<unique_ptr<Book>> library;
    library.push_back(make_unique<EBook>(
        "C++ 20 Guide", "J. Doe", 100.0, 520, 0.10, true));
    library.push_back(make_unique<PrintedBook>(
        "Clean Code", "R. Martin", 150.0, 464, Cover::Hard, 20.0, 0.05));

    for (const auto &bk : library) {
        // Book* tabanından sanal çağrı → gerçek türe göre çalışır
        bk->PrintInfo();
    }
}

unique_ptr<Book> ile bellek yönetimi otomatikleşir; delete gerekmez.


5. Downcasting: dynamic_cast Kullanımı

Bazen taban tip referansından asıl türe geri dönmek gerekebilir. Güvenli dönüşüm için dynamic_cast kullanılır.


void TryPrintDRM(const Book* b) {
    if (auto eb = dynamic_cast<const EBook*>(b)) {
        cout << "EBook DRM durumu kontrol edildi." << endl;
    } else {
        cout << "EBook degil." << endl;
    }
}

dynamic_cast başarısız olursa nullptr döner (pointer kullanırken). Bu sayede tür güvenliği korunur. RTTI kapalı veya uygun sanal tablo yoksa kullanılamaz.


6. Abstract (Soyut) Taban Sınıf

En az bir saf sanal (pure virtual) fonksiyon içeren sınıflar soyuttur ve doğrudan örneklenemez. Book’u soyutlayarak mimariyi zorunlu sözleşmeyle netleştirebiliriz.


class AbstractBook {
protected:
    string title, author;
    int pageCount;

public:
    AbstractBook(string t, string a, int p)
        : title(t), author(a), pageCount(p) {}
    virtual ~AbstractBook() = default;

    virtual double GetFinalPrice() const = 0; // saf sanal → soyut kurar
    virtual void PrintInfo() const = 0;
};

// Türetilmiş sınıf saf sanalları mutlaka override eder
class SimpleEBook : public AbstractBook {
    double basePrice, vat;
public:
    SimpleEBook(string t, string a, int p, double price, double v)
        : AbstractBook(t,a,p), basePrice(price), vat(v) {}

    double GetFinalPrice() const override { return basePrice*(1+vat); }
    void PrintInfo() const override {
        cout << "[SimpleEBook] " << title
             << " | Price: " << GetFinalPrice() << endl;
    }
};

Soyut taban sınıf, projede sözleşme odaklı tasarım sağlar (interface benzeri yapı).


7. Erişim Belirleyicileri ve Kalıtım Türleri

Kalıtımda public, protected, private türleri vardır.

Uygulamada genellikle public inheritance tercih edilir (is-a ilişkisi).


8. final ve override Anahtar Sözcükleri

override alt sınıfın gerçekten sanal bir metodu ezdiğini derleyiciye bildirir. final ise bir metodun veya sınıfın daha fazla ezilemeyeceğini belirtir.


class FinalEBook : public EBook {
public:
    using EBook::EBook;
    double GetFinalPrice() const override final {
        return EBook::GetFinalPrice(); // daha fazla override edilemez
    }
};

9. Tam Uygulama Örneği


#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;

enum class Cover { Soft, Hard };

class Book {
protected:
    string title, author;
    double basePrice;
    int pageCount;

public:
    Book(string t, string a, double price, int pages)
        : title(t), author(a), basePrice(price), pageCount(pages) {}
    virtual ~Book() = default;

    virtual double GetFinalPrice() const { return basePrice; }
    virtual void PrintInfo() const {
        cout << "[Book] " << title
             << " | " << author
             << " | " << pageCount << " pages"
             << " | " << GetFinalPrice() << " TL" << endl;
    }
};

class EBook : public Book {
    double vatRate; bool drmProtected;
public:
    EBook(string t, string a, double price, int pages, double vat, bool drm)
        : Book(t,a,price,pages), vatRate(vat), drmProtected(drm) {}
    double GetFinalPrice() const override {
        double x = basePrice*(1+vatRate);
        if (pageCount >= 500) x *= 0.95;
        return x;
    }
    void PrintInfo() const override {
        cout << "[EBook] " << title
             << " | DRM: " << (drmProtected? "Yes":"No")
             << " | " << GetFinalPrice() << " TL" << endl;
    }
};

class PrintedBook : public Book {
    Cover coverType; double shipping; double printCost;
public:
    PrintedBook(string t, string a, double price, int pages,
                Cover cover, double ship, double perPage)
        : Book(t,a,price,pages), coverType(cover),
          shipping(ship), printCost(perPage) {}
    double GetFinalPrice() const override {
        double c = basePrice + shipping + printCost*pageCount;
        if (coverType == Cover::Hard) c *= 1.10;
        return c;
    }
    void PrintInfo() const override {
        cout << "[PrintedBook] " << title
             << " | Cover: " << (coverType==Cover::Hard?"Hard":"Soft")
             << " | " << GetFinalPrice() << " TL" << endl;
    }
};

int main() {
    vector<unique_ptr<Book>> books;
    books.push_back(make_unique<EBook>("Modern C++", "A. Dev", 120.0, 520, 0.10, true));
    books.push_back(make_unique<PrintedBook>("Clean Code", "R. Martin", 150.0, 464, Cover::Hard, 25.0, 0.04));

    for (const auto& b : books) b->PrintInfo(); // polimorfik çağrı

    // Güvenli downcast denemesi
    if (auto pb = dynamic_cast<PrintedBook*>(books[1].get())) {
        cout << "PrintedBook fiyatı: " << pb->GetFinalPrice() << endl;
    }
}

Kod, Visual Studio 2022 ve GCC 11+ üzerinde derlenip çalıştırılabilir.


10. TL;DR

  • Kalıtım ile ortak davranışları taban sınıfta toplar, alt sınıflarda özelleştiririz.
  • virtual metotlar ve override anahtar sözcüğü dinamik polimorfizm sağlar.
  • Virtual destructor olmadan taban pointer üzerinden silme tehlikelidir.
  • Constructor zinciri: önce taban sınıf kurucusu, sonra türetilmiş sınıf kurucusu çalışır.
  • dynamic_cast ile güvenli downcasting yapılabilir (başarısızsa nullptr).
  • Soyut taban sınıf (pure virtual) doğrudan örneklenemez, sözleşme dayatır.
  • Örnekler Visual Studio 2022 ve GCC 11+ ile uyumludur.

İlişkili Makaleler