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.
- public kalıtım: Tabanın public üyeleri alt sınıfta da public kalır (en yaygın).
- protected kalıtım: Tabanın public üyeleri alt sınıfta protected olur (dışarıya kapanır).
- private kalıtım: Tabanın tüm public/protected üyeleri alt sınıfta private olur.
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.
virtualmetotlar veoverrideanahtar 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_castile güvenli downcasting yapılabilir (başarısızsanullptr).- Soyut taban sınıf (pure virtual) doğrudan örneklenemez, sözleşme dayatır.
- Örnekler Visual Studio 2022 ve GCC 11+ ile uyumludur.