Menu
Coddy logo textTech

Sanal Kalıtım

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

Elmas problemi (diamond problem), bir sınıfın ortak bir temel sınıfı paylaşan iki sınıftan miras alması durumunda ortaya çıkar. Özel bir işlem yapılmadığında, türetilmiş sınıf ortak temel sınıfın iki kopyasına sahip olur; bu da belirsizliğe ve bellek israfına yol açar.

class Animal {
public:
    int age;
};

class Mammal : public Animal {};
class Bird : public Animal {};

class Bat : public Mammal, public Bird {};

Bat b;
b.age = 5;  // Hata: belirsiz - hangi 'age'?

Bat sınıfı, biri Mammal ve diğeri Bird üzerinden olmak üzere iki ayrı Animal alt nesnesi içerir. Bu, probleme adını veren elmas biçimli kalıtım diyagramını oluşturur.

Sanal kalıtım (virtual inheritance), ortak taban sınıfın yalnızca bir kopyasının bulunmasını sağlayarak bunu çözer. Paylaşılan taban sınıftan kalıtım alırken virtual anahtar kelimesini ekleyin:

class Animal {
public:
    int age;
    Animal(int a = 0) : age(a) {}
};

class Mammal : virtual public Animal {
public:
    Mammal(int a = 0) : Animal(a) {}
};

class Bird : virtual public Animal {
public:
    Bird(int a = 0) : Animal(a) {}
};

class Bat : public Mammal, public Bird {
public:
    Bat(int a) : Animal(a), Mammal(a), Bird(a) {}
};

Bat b(5);
b.age = 10;  // Çalışır! Sadece bir tane 'age' mevcuttur

Bat'in, Animal'ı kurucusunda doğrudan başlatması gerektiğine dikkat edin. Sanal kalıtım ile, ara sınıflardan bağımsız olarak sanal tabanı oluşturmaktan en çok türetilmiş sınıf sorumludur.

challenge icon

Görev

Kolay

Sanal kalıtımın (virtual inheritance) elmas problemini (diamond problem) nasıl çözdüğünü gösteren bir çalışan yönetim sistemi kuralım. Hem Developer hem de Manager sınıflarından miras alan ve her ikisi de ortak bir Employee temel sınıfını paylaşan bir TeamLead hiyerarşisi oluşturacaksınız.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • Employee.h: Ortak temel sınıf olan Employee sınıfını şu özelliklerle tanımlayın:
    • Korumalı (protected) bir std::string name ve int id
    • Her iki değeri de alan ve şunu yazdıran bir yapıcı (constructor): Employee [<name>] hired with ID <id>
    • Şunu yazdıran genel (public) bir getInfo() metodu: Employee: <name> (ID: <id>)
    • Şunu yazdıran sanal bir yıkıcı (virtual destructor): Employee [<name>] record closed
  • Developer.h: Employee sınıfından virtual public inheritance (sanal genel kalıtım) kullanan bir Developer sınıfı tanımlayın:
    • Korumalı bir std::string language üyesi
    • İsim, id ve dil alan bir yapıcı — isim ve id'yi Employee sınıfına iletir, dili saklar ve şunu yazdırır: Developer [<name>] specializes in <language>
    • Şunu yazdıran genel bir code() metodu: <name> is coding in <language>
    • Şunu yazdıran bir yıkıcı: Developer [<name>] signed off
  • Manager.h: Employee sınıfından virtual public inheritance kullanan bir Manager sınıfı tanımlayın:
    • Korumalı bir int teamSize üyesi
    • İsim, id ve ekip boyutu alan bir yapıcı — isim ve id'yi Employee sınıfına iletir, ekip boyutunu saklar ve şunu yazdırır: Manager [<name>] leads a team of <teamSize>
    • Şunu yazdıran genel bir manage() metodu: <name> is managing <teamSize> people
    • Şunu yazdıran bir yıkıcı: Manager [<name>] stepped down
  • main.cpp: Dört girdi okuyun (her biri ayrı bir satırda):
    1. İsim (string)
    2. Çalışan ID'si (tamsayı)
    3. Programlama dili (string)
    4. Ekip boyutu (tamsayı)

    Hem Developer hem de Manager sınıflarından genel olarak miras alan bir TeamLead sınıfı tanımlayın:

    • Dört parametrenin tamamını alan ve Employee (sanal temel sınıf), ardından Developer ve Manager sınıflarını doğrudan başlatması gereken bir yapıcı
    • Yapıcı şunu yazdırmalıdır: TeamLead [<name>] ready to lead and code!
    • Sırasıyla getInfo(), code() ve manage() metodlarını çağıran bir showRole() metodu
    • Şunu yazdıran bir yıkıcı: TeamLead [<name>] promoted out

    Bir blok kapsamı (block scope) içinde bir TeamLead nesnesi oluşturun, showRole() metodunu çağırın ve ardından nesnenin kapsam dışına çıkmasını sağlayın. Bloktan sonra şunu yazdırın: Organization restructured!

Örneğin, Alice, 101, C++ ve 5 girdileriyle:

Employee [Alice] hired with ID 101
Developer [Alice] specializes in C++
Manager [Alice] leads a team of 5
TeamLead [Alice] ready to lead and code!
Employee: Alice (ID: 101)
Alice is coding in C++
Alice is managing 5 people
TeamLead [Alice] promoted out
Manager [Alice] stepped down
Developer [Alice] signed off
Employee [Alice] record closed
Organization restructured!

Yalnızca bir Employee yapıcı çağrısı ve bir Employee yıkıcı çağrısı olduğuna dikkat edin — sanal kalıtım, paylaşılan temel sınıfın yalnızca bir kopyasının var olmasını sağlar. TeamLead, Employee sınıfını doğrudan başlatmalıdır çünkü sanal kalıtımda, en çok türetilen sınıf (most derived class) sanal temel sınıfı oluşturmaktan sorumludur.

Kopya kağıdı

Elmas problemi (diamond problem), bir sınıfın ortak bir temel sınıfa sahip iki sınıftan miras alması durumunda ortaya çıkar; bu durum temel sınıfın iki kopyasının oluşmasına ve belirsizliğe yol açar.

class Animal {
public:
    int age;
};

class Mammal : public Animal {};
class Bird : public Animal {};

class Bat : public Mammal, public Bird {};

Bat b;
b.age = 5;  // Error: ambiguous - which 'age'?

Sanal kalıtım (virtual inheritance), ortak temel sınıfın yalnızca bir kopyasının bulunmasını sağlayarak elmas problemini çözer. Miras alırken virtual anahtar kelimesini kullanın:

class Animal {
public:
    int age;
    Animal(int a = 0) : age(a) {}
};

class Mammal : virtual public Animal {
public:
    Mammal(int a = 0) : Animal(a) {}
};

class Bird : virtual public Animal {
public:
    Bird(int a = 0) : Animal(a) {}
};

class Bat : public Mammal, public Bird {
public:
    Bat(int a) : Animal(a), Mammal(a), Bird(a) {}
};

Bat b(5);
b.age = 10;  // Works! Only one 'age' exists

Sanal kalıtım kullanıldığında, ara sınıflardan bağımsız olarak, en alt türetilmiş sınıf (most derived class) sanal temel sınıfı kendi yapıcı metodunda (constructor) doğrudan başlatmalıdır.

Kendin dene

#include <iostream>
#include <string>
#include "Developer.h"
#include "Manager.h"

using namespace std;

// TODO: Hem Developer hem de Manager sınıflarından genel (public) olarak miras alan TeamLead sınıfını tanımlayın
// Unutmayın: Sanal miras (virtual inheritance) ile TeamLead, Employee (sanal temel sınıf) sınıfını doğrudan başlatmalıdır
class TeamLead : public Developer, public Manager {
public:
    // TODO: name, id, language ve teamSize parametrelerini alan kurucuyu (constructor) uygulayın
    // Şu sırayla başlatılmalıdır: Önce Employee (sanal temel sınıf), ardından Developer, sonra Manager
    // Şunu yazdırmalıdır: TeamLead [<name>] ready to lead and code!
    TeamLead(const std::string& name, int id, const std::string& language, int teamSize)
        : Employee(name, id),
          Developer(name, id, language),
          Manager(name, id, teamSize) {
        // TODO: Kurucu mesajını yazdırın
    }

    // TODO: showRole() metodunu uygulayın
    // Bu sırayla getInfo(), code() ve manage() metodlarını çağırmalıdır
    void showRole() {
        // TODO: Üç metodu çağırın
    }

    // TODO: Yıkıcıyı (destructor) uygulayın
    // Şunu yazdırmalıdır: TeamLead [<name>] promoted out
    ~TeamLead() {
        // TODO: Yıkıcı mesajını yazdırın
    }
};

int main() {
    // Girdileri oku
    string name;
    int id;
    string language;
    int teamSize;

    getline(cin, name);
    cin >> id;
    cin.ignore();
    getline(cin, language);
    cin >> teamSize;

    // TODO: Bir blok kapsamı (block scope) içinde bir TeamLead nesnesi oluşturun
    // showRole() metodunu çağırın, ardından kapsam dışına çıkmasına izin verin
    {
        // TODO: TeamLead oluşturun ve showRole() metodunu çağırın
    }

    // Bloktan sonra son mesajı yazdırın
    cout << "Organization restructured!" << endl;

    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