class Bird {
public:
size_t getWeight() const { return weight_; }
size_t getHeight() const { return height_; }
std::string getName() const { return name_; }
// Pure virtual function without implementation
virtual void eat() = 0;
virtual void sleep() = 0;
protected:
size_t weight_;
size_t height_;
std::string name_;
};Metody eat() oraz sleep() to tzw. metody czysto wirtualne (ang. pure virtual functions). Świadczy o tym = 0;. Oznacza to, że nigdzie nie znajdziemy ich implementacji dla klasy Bird. Klasy które dziedziczą po Bird będą ją musiały zaimplementować same.
Znaczenie słowa virtual na razie przemilczymy. Jedyne co trzeba na teraz wiedzieć, to aby metoda była czysto wirtualna = 0; to musi być przed nią słowo virtual.
Jednym z pomysłów na rozwiązanie problemu wielodziedziczenia jest tworzenie tzw. interfejsów, ich dziedziczenie oraz przeciążanie implementacji metod z klas bazowych. Interfejsy określają "umiejętności" i łatwo je ze sobą łączyć.
class Flyable {
public:
virtual void fly() = 0;
private:
Wings wings_;
};
class Swimmable {
public:
virtual void swim() = 0;
};
class Soundable {
public:
virtual void makeSound() = 0;
};class Penguin : public Bird,
public Swimmable,
public Soundable {
public:
// Override from Bird
void eat() override;
void sleep() override;
// Override from Swimmable
void swim() override;
// Override from Soundable
void makeSound() override;
};
class Hummingbird : public Bird,
public Flyable {
public:
// Override from Bird
void eat() override;
void sleep() override;
// Override from Flyable
void fly() override;
};
class Goose : public Bird,
public Flyable,
public Soundable,
public Swimmable {
public:
// Override from Bird
void eat() override;
void sleep() override;
// Override from Soundable
void makeSound() override;
// Override from Flyable
void fly() override;
// Override from Swimmable
void swim() override;
};Interfejs to zestaw funkcji, które klasa implementująca go musi zaimplementować (masło maślane).
Interfejs to zestaw funkcji, które klasa dziedzicząca po nim musi zaimplementować.
Brak implementacji funkcji czysto wirtualnej to błąd linkera (undefined reference).
W pełni poprawna definicja interfejsu to część publiczna klasy / zestawu funkcjonalności. Mogą to być zarówno metody, pola, typy, ale najczęściej będziemy słowa interfejs używać w odniesieniu do publicznych metod klasy.
Zobacz interfejs wektora na cppreference.com
Znajdziesz tam opis jego publicznych metod (member functions), typów wewnętrznych (member types) oraz luźnych funkcji, które go wykorzystują (non-member functions).
Nie można utworzyć obiektu klasy, która posiada co najmniej jedną funkcję czysto wirtualną.
Funkcja czysto wirtualna nie ma implementacji, więc nie może istnieć obiekt, dla którego linker nie znajdzie implementacji jednej z jego funkcji.
Możemy przechowywać wskaźnik wskazujący na typ tej klasy, ale nie możemy stworzyć jej instancji (obiektu), ponieważ nie mamy zdefiniowanych jej zachowań.
Klasa taka nazywa się klasą abstrakcyjną i służy tylko do ujednolicania interfejsu, a nie tworzenia obiektów.
Dopiero obiekt klasy pochodnej, która zaimplementuje wszystkie brakujące metody, może zostać utworzony.
Co to za słowa? Co one robią? O tym w jednym z następnych wideo ;)