Skip to content

Instantly share code, notes, and snippets.

@NickTikhomirov
Last active June 19, 2019 18:07
Show Gist options
  • Save NickTikhomirov/8971d0fa95c5cd318f906d890d87c956 to your computer and use it in GitHub Desktop.
Save NickTikhomirov/8971d0fa95c5cd318f906d890d87c956 to your computer and use it in GitHub Desktop.
Наследование

Вопросы 13-18, Наследование

Автор: Толик и Никита

Головной раздел: Меню

Билет 13. Наследование классов. Объявление производного класса, пример использования.

Основная идея: на базе существующего класса (называется родитель или базовый), создается производный класс (наследник или дочерний), который включает в себя поля и функции базового класса (наследует их), и содержит дополнительные поля (обладает новыми свойствами) и функции.

Для наследования после названия класса ставится двоеточие, и дальше имя класса, от которого наследуемся:

  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));
    }
  };

__________________________________________________________________

Вопрос 14. Статусы доступа при наследовании классов.

Перед именем базового класса может указываться статус доступа наследования или тип наследования доступа (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

__________________________________________________________________

Вопрос 15. Конструкторы и деструктор в производных классах, примеры.

Конструктор производного класса всегда должен вызывать конструктор базового класса. Если это действие не написано программистом, то вызывается конструктор без параметров (если его нет, вылезает ошибка). Если класс имеет несколько базовых, то конструкторы базовых классов должны вызываться в порядке перечисления этих классов в списке базовых.

Деструктор производного класса всегда неявно по умолчанию после выполнения своего тела вызывает деструкторы базовых классов. Причем порядок вызова деструкторов обратен порядку вызова конструкторов.

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;
    }
}

__________________________________________________________________

Вопрос 16. Множественное наследование и виртуальные базовые классы, примеры.

Множественное наследование - наследование более чем от одного класса

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;

__________________________________________________________________

Вопрос 18. Абстрактные и локальные классы в Си++, примеры.

Абстрактный класс - класс, содержащий хотя бы одну чистую виртуальную функцию. Объект такого класса нельзя создать, но можно создать указатель на него.

class AbstractItem
{
    int weigth;
    int amount;

public:
    virtual std::string getItemName() const = 0;
}

Локальный класс - класс, объявленный не в глобальной области (в блоке или в теле другого класса)

int main()
{
    struct A
    {
        int a = 0;
        int b = 0;
    };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment