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, bir sınıf ortak bir temel sınıfı paylaşan iki sınıftan miras aldığında ortaya çıkar. Özel bir işlem uygulanmadığında, türetilmiş sınıf ortak temelin iki kopyasına sahip olur; bu da belirsizliğe ve bellek israfına neden olur.
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ı iki ayrı Animal alt nesnesi içerir: biri Mammal üzerinden, diğeri ise Bird üzerinden. Bu, probleme adını veren elmas biçimli kalıtım diyagramını oluşturur.
Sanal kalıtım, ortak base sınıfın yalnızca bir kopyasının bulunmasını sağlayarak bu sorunu çözer. Paylaşılan base sınıftan kalıtım alırken virtual anahtar sözcüğünü 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ışıyor! Yalnızca bir 'age' varBat öğesinin constructor içinde Animal öğesini directly başlatması gerektiğine dikkat edin. virtual inheritance ile en türetilmiş sınıf, ara sınıflardan bağımsız olarak sanal temel sınıfı oluşturmaktan sorumludur.
Görev
KolayElmas problemini sanal kalıtımın nasıl çözdüğünü gösteren bir çalışan yönetim sistemi oluşturalım. TeamLead'in, her ikisi de ortak bir Developer temel sınıfını paylaşan Manager ve Employee sınıflarından kalıtım aldığı bir hiyerarşi oluşturacaksın.
Kodunu dört dosyaya ayıracaksın:
Employee.h: Şunlara sahip ortak temel sınıfEmployee'ı tanımla:- Korumalı bir
std::string nameveint id - Her iki değeri alan ve şunu yazdıran bir kurucu:
Employee [<name>] hired with ID <id> - Şunu yazdıran herkese açık bir
getInfo()metodu:Employee: <name> (ID: <id>) - Şunu yazdıran sanal bir yıkıcı:
Employee [<name>] record closed
- Korumalı bir
Developer.h:Developer'dan sanal public kalıtım kullanan birEmployeesınıfı tanımla:- Korumalı bir
std::string languageüyesi - name, id ve language alan bir kurucu: name ve id değerlerini
Employee'a aktarır, language değerini saklar ve şunu yazdırır:Developer [<name>] specializes in <language> - Şunu yazdıran herkese açık bir
code()metodu:<name> is coding in <language> - Şunu yazdıran bir yıkıcı:
Developer [<name>] signed off
- Korumalı bir
Manager.h:Manager'dan sanal public kalıtım kullanan birEmployeesınıfı tanımla:- Korumalı bir
int teamSizeüyesi - name, id ve team size alan bir kurucu: name ve id değerlerini
Employee'a aktarır, team size değerini saklar ve şunu yazdırır:Manager [<name>] leads a team of <teamSize> - Şunu yazdıran herkese açık bir
manage()metodu:<name> is managing <teamSize> people - Şunu yazdıran bir yıkıcı:
Manager [<name>] stepped down
- Korumalı bir
main.cpp: Dört girdi oku (her biri ayrı bir satırda):- Ad (string)
- Çalışan kimliği (tam sayı)
- Programlama dili (string)
- Ekip büyüklüğü (tam sayı)
Hem
TeamLeadhem deDeveloper'dan public olarak kalıtım alan birManagersınıfı tanımla:- Dört parametrenin tümünü alan ve önce doğrudan
Employee'ı (sanal temel sınıf), ardındanDeveloperveManager'ı başlatması gereken bir kurucu - Kurucu şunu yazdırmalı:
TeamLead [<name>] ready to lead and code! showRole(),getInfo()vecode()metotlarını bu sırayla çağıran birmanage()metodu- Şunu yazdıran bir yıkıcı:
TeamLead [<name>] promoted out
Bir blok kapsamı içinde bir
TeamLeadnesnesi oluştur,showRole()metodunu çağır, ardından kapsamından çıkmasına izin ver. Bloktan sonra şunu yazdır:Organization restructured!
Örneğin girdiler Alice, 101, C++ ve 5 olduğunda:
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 kurucu çağrısı ve bir Employee yıkıcı çağrısı olduğuna dikkat et: sanal kalıtım, paylaşılan temel sınıfın yalnızca bir kopyasının bulunmasını sağlar. Sanal kalıtımda sanal temel sınıfı oluşturmak en türetilmiş sınıfın sorumluluğunda olduğundan, TeamLead doğrudan Employee'ı 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'dan public olarak miras alan TeamLead sınıfını tanımla
// Unutma: Sanal miras ile TeamLead, Employee'yi (sanal taban) doğrudan başlatmalıdır
class TeamLead : public Developer, public Manager {
public:
// TODO: name, id, language ve teamSize alan yapıcıyı uygula
// Başlatılmalı: önce Employee (sanal taban), sonra Developer, sonra Manager
// Should print: 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: Yapıcı mesajını yazdır
}
// TODO: showRole() metodunu uygula
// getInfo(), code() ve manage() metodlarını bu sırayla çağırmalı
void showRole() {
// TODO: Üç metodu çağır
}
// TODO: Yıkıcıyı uygula
// Should print: TeamLead [<name>] promoted out
~TeamLead() {
// TODO: Yıkıcı mesajını yazdır
}
};
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ında bir TeamLead nesnesi oluştur
// showRole()'u çağır, sonra kapsam dışına çıkmasına izin ver
{
// TODO: TeamLead oluştur ve showRole()'u çağır
}
// Bloğun ardından son mesajı yazdır
cout << "Organization restructured!" << endl;
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 KonteynerKendi başına pratik yap: Online C++ derleyicisi