Наследование - это механизм, позволяющий создавать производные (дочерние) классы на основе уже существующих (родительских).
struct Person // родительский класс
{
// Геттеры
string name() const {return name_;}
int age() const {return age_;}
private:
string name_;
int age_;
};
struct Student : Person // дочерний класс
{
string university() const {return uni_;}
private:
string uni_;
};
У дочернего класса можно вызывать публичные методы родительского класса:
Student s;
std::cout << s.name() << " " << s.age() << " " << s.university() << std::endl;
Поля добавленные в дочернем классе инициализируются в конструкторе дочернего класса, а те поля, которые унаследованны от родительского класса инициализируются внутри конструктора родительского класса. При создании объекта произвольного класса сначала вызывается конструктор родительского класса, если у родительского класса нет конструктора по умолчанию, но есть конструктор с параметрами, то его нужно вызывать самостоятельно (если есть конструктор по умолчанию, то он вызовется сам):
struct Person
{
Person(string name, int age) : name(name), age(age)
{}
...
};
struct Student : Person
{
Student(string name, int age, string uni) : Person(name, age), uni_(uni)
{}
...
};
Аналогично работает деструктор: при удалении экземпляра Student, вначале вызовется деструктор Person. Это делается автоматически и на это никак нельзя повлиять.
Для дочерних классов определены следующие приведения:
Student s("Alex", 21, "Oxford");
// ссылка на Student -> ссылка на базовый класс
Person & l = s; // Student & приводится к Person &
// указатель на Student -> указатель на базовый класс
Person * r = &s; // Student * приводится к Person *
В обратную сторону такие приведения не работают!
Благодаря приведению ссылок, мы можем инициализировать родительский класс при помощи дочернего. При этом копируются только поля родительского класса (такое поведение называется - срезка):
Student s("Alex", 21, "Oxford");
Person p = s; // Person("Alex", 21);
Будет вызван только конструктор копирования Person(Person const& p);
, который ничего не знает про uni_.
Protected
Если в родительском классе поля/методы были объявлены с модификатором private, то внутри дочернего класса невозможно будет к ним обратиться.
Если нам потребуется определить поля/методы так, что бы наследники к ним имели доступ, а внешний код нет, то нужно использовать модификатор protected:
struct Person
{
protected:
string name_;
int age_;
};
struct Student : Person
{
// у Student есть доступ к полям name_ и age_
};
Синтаксис наследования
В общем случае синтаксис наследования выглядит так:
struct Child : <modifier> Parent {};
где modifier это одно из ключевых слов: public, protected, private.
Если не указать модификатор, то будет использован модификатор по умолчанию, для структур это public, для классов - private. При этом совершенно не важно является ли Parent классом или структурой, все зависит от дочернего Child.