Автор: Толик и Никита
Головной раздел: Меню
Основная идея: на базе существующего класса (называется родитель или базовый), создается производный класс (наследник или дочерний), который включает в себя поля и функции базового класса (наследует их), и содержит дополнительные поля (обладает новыми свойствами) и функции.
Для наследования после названия класса ставится двоеточие, и дальше имя класса, от которого наследуемся:
class A{
(...)
};
class B: A{
(...)
};
Перед именем базового класса может указываться статус доступа наследования или тип наследования доступа (public,protected, private). Это определяет статус доступа наследуемых полей и методов из родителя внутри производного. Производный класс может определяться с ключевым словом struct или class (с union нельзя). Если производный класс определен с ключевым словом class, то по умолчанию статус доступа наследования private. Если struct, то public.
Пример:
class Square{
int a;
short sides;
public:
Square(int arg){ a=arg; sides=4;}
int getP() {return a*sides;}
int getS() {return a*a;}
short getSides(){return sides}
};
class Triangle: public Square{ //ДА, БЛ*ТЬ, Я НАСЛЕДУЮ ТРЕУГОЛЬНИК ОТ КВАДРАТА! И ЧТО ТЫ МНЕ СДЕЛАЕШЬ?
int b;
int c;
public:
Trinagle(int pa, int pb, int c){
a=pa;
b=pb;
c=pc;
sides=3;
}
int getP(){ return a+b+c;}
int getS(){
double p=1.0*getP()/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
};
Перед именем базового класса может указываться статус доступа наследования или тип наследования доступа (public,protected,private). Это определяет статус доступа наследуемых полей и методов из родителя внутри производного. Производный класс может определяться с ключевым словом struct или class (с union нельзя). Если производный класс определен с ключевым словом class, то по умолчанию статус доступа наследования private. Если struct, то public.
исходный -> | public | protected | private |
---|---|---|---|
public-наследование | public | protected | private |
protected-наследование | protected | protected | private |
private-наследование | private | private | private |
Конструктор производного класса всегда должен вызывать конструктор базового класса. Если это действие не написано программистом, то вызывается конструктор без параметров (если его нет, вылезает ошибка). Если класс имеет несколько базовых, то конструкторы базовых классов должны вызываться в порядке перечисления этих классов в списке базовых.
Деструктор производного класса всегда неявно по умолчанию после выполнения своего тела вызывает деструкторы базовых классов. Причем порядок вызова деструкторов обратен порядку вызова конструкторов.
class Base
{
int *first;
public:
Base(int size)
: first(new int[size])
{}
virtual ~Base()
{
delete[] first;
}
}
class Derived
{
int *second;
public:
// Сначала вызовется конструктор базового класса
Derived(int size)
: Base(size), second(new int[size])
{}
// Сначала будет вызван деструктор производного класса
virtual ~Derived()
{
delete[] second;
}
}
Множественное наследование - наследование более чем от одного класса
struct Base
{
int a = 5;
};
// Применим виртуальное наследование
struct A: virtual public Base
{
int b = 6;
};
// Применим виртуальное наследование
struct B: virtual public Base
{
int c = 8;
};
struct Z: public A, public B
{
int getSum() const
{
return a + b + c;
}
};
Виртуальное наследование утяжеляет класс на один указатель (таблица виртуальных классов), т.е. на sizeof(int).
Однако, если не наследоваться виртуально, то класс Z
будет содержать два экземпляра Base, и обращение z->a будет кидать ошибку, потому что клас Z
получил a от Base и через A
, и через B
, и ему непонятно, про какую из двух a вы говорите.
Вопрос 17. Переопределение функций при наследовании классов, виртуальные функции в Си++ (таблица виртуальных функций). Статическое и динамическое связывание. Примеры.
class Base
{
public:
virtual int dynamicLink() const
{
return 5;
}
int staticLink() const
{
return -5;
}
};
class Derived
{
public:
int dynamicLink() const override
{
return Base::dynamicLink() + 4;
}
int staticLink() const
{
return Base::staticLink() - 4;
}
}
При наличии хотя бы одной виртуальной функции в класс будет заложен указатель на таблицу виртуальных функций, через которую происходит динамическое связывание.
// Где-то в коде
Derived d;
// Напечатает 9, так как произошло динамическое связывание
std::cout << ((Base *)(&d))->dynamicLink() << std::endl;
// Напечатает -5, так как произошло статическое связывание
// Т.к. метод staticLink не виртуальный
std::cout << ((Base *)(&d))->staticLink() << std::endl;
Абстрактный класс - класс, содержащий хотя бы одну чистую виртуальную функцию. Объект такого класса нельзя создать, но можно создать указатель на него.
class AbstractItem
{
int weigth;
int amount;
public:
virtual std::string getItemName() const = 0;
}
Локальный класс - класс, объявленный не в глобальной области (в блоке или в теле другого класса)
int main()
{
struct A
{
int a = 0;
int b = 0;
};
}