Лабораторная работа №2: Программирование с использованием наследования классов и виртуальных функций

Цель работы: 1) изучить возможности наследования классов на языке С++; 2) получить основные навыки программирования с использованием наследования классов. Теоретические сведения

Язык С++ позволяет классу наследовать данные-элементы и функции-элементы одного или нескольких других классов. Новый класс называют производным классом. Класс, элементы которого наследуются производным классом, называется базовым классом. В свою очередь производный класс может служить базовым для другого класса. Наследование дает возможность заключить некоторое общее или схожее поведение различных объектов в одном базовом классе.

Наследование позволяет также изменить поведение существующего класса. Производный класс может переопределить некоторые функции-элементы базового, наследуя, тем не менее, основной объем свойств и атрибутов базового класса. Общий вид наследования:

class Base {
    // ...
};
class Derived: <ключ доступа> Base {
    // .....
};

Ключ доступа может быть private, protected, public. Если ключ не указан, то по умолчанию он принимается private. Наследование позволяет рассматривать целые иерархии классов и работать со всеми элементами одинаково, приводя их к базовому. Правила приведения следующие: Наследуемый класс всегда можно привести к базовому;

Базовый класс можно привести к наследуемому только если в действительности это объект наследуемого класса. Ошибки приведения базового класса к наследуемому отслеживаются программистом.

Доступ к элементам класса

При наследовании ключ доступа определяет уровень доступа к элементам базового класса внутри производного класса. В таблице описаны возможные варианты доступа.

Наследование Доступ в базовом классе Доступ в производном классе
public public protected private public protected private
protected public protected private protected protected private
private public protected private private private private

Конструкторы и деструкторы при наследовании

Конструкторы не наследуются. Если конструктор базового класса требует спецификации одного или нескольких параметров, конструктор производного класса должен вызывать базовый конструктор, используя список инициализации элементов. Пример 1.

#include <string>
class Base {
public:
    Base(int, float);
};
class Derived: Base
{    public:
    Derived(char* lst, float amt);
};
Derived::Derived(char* lst, float amt) : Base(strlen(lst),amt)
{ }

В деструкторе производного класса компилятор автоматически генерирует вызовы базовых деструкторов, поэтому для удаления объекта производного класса следует сделать деструктор в базовых классах виртуальным. Для вызова используется delete this либо operator delete.

Виртуальные функции

Функция-элемент может быть объявлена как virtual. Ключевое слово virtual предписывает компилятору генерировать некоторую дополнительную информацию о функции. Если функция переопределяется в производном классе и вызывается с указателем (или ссылкой) базового класса, ссылающимся на представитель производного класса, эта информация позволяет определить, какой из вариантов функции должен быть выбран: такой вызов будет адресован функции производного класса.

Для виртуальных функций существуют следующие правила: виртуальную функцию нельзя объявлять как static. спецификатор virtual необязателен при переопределении функции в производном классе. виртуальная функция должна быть определена в базовом классе и может быть переопределена в производном.

Пример программирования Пример 2. Написать программу с наследованием класса стек от класса массив.

#include <iostream>
#include <cstdlib>
class massiv {
    int *num;
    int kol;
    public:
    massiv(int n);
    void print();
    virtual int kolich() { return kol; }
    void put(int k,int n) { num[k]=n; }
    ~massiv() { delete[] num; }
};
massiv::massiv(int n) {
    num = new int[n];
    kol  =n;
    for (int i=0; i < kol; i++)
        num[i] = random(100) - 50;
}
void massiv::print() {
    for (int i = 0; i < kolich(); i++)
        std::cout << num[i] << " ";
    std::cout << std::endl;
}
class stec : public massiv {
    int top;
public:
    stec(int);
    virtual int kolich() {return top;}
    void pop(int k);
};
stec::stec(int n):massiv(n) {
    top=0;
}
void stec::pop(int k) {
    put(top++,k);
}
int main() {
    randomize();
    massiv a(10);
    a.print();
    stec b(10);
    b.pop(random(100)-50);
    b.pop(random(100)-50);
    b.pop(random(100)-50);
    b.print();
}

Главное отличие виртуальной функции от просто перегруженной в том, какая функция будет вызываться при рассмотрении производного класса как базового. Пример 3.

#include <iostream>
class Base {
public:
    Base() {}
    Print() { std::cout << "I'm a Base print" << std::endl; }
    virtual View(){ std::cout << "I'm a Base view" << std::endl; }
};
class Derived: public Base {
public:
    Derived(){};
    Print(){ std::cout << "I'm a Derived print" << std::endl; }
    View(){ std::cout << "I'm a Derived view" << std::endl; }
};
int main(void) {
    Base *A=new Base;
    Derived *B=new Derived;
    Base *C;
    A->Print();
    A->View();
    B->Print();
    B->View();
    C=(Base *)B;
    C->Print();
    C->View();
    return 0;
}

Результат: "I'm a Base print" "I'm a Base view" "I'm a Derived print" "I'm a Derived view" "I'm a Base print" "I'm a Derived view"

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

Контрольные вопросы

  1. Какой класс называется базовым?
  2. Какой класс называется производным?
  3. Какие ключи доступа используются при наследовании?
  4. Наследуются ли конструкторы?
  5. Наследуются ли деструкторы?
  6. Что собой представляет виртуальная функция?
  7. Можно ли виртуальную функцию объявить как static?

Варианты заданий

  1. Разработать программу с использованием наследования классов, реализующую классы: графический объект; круг; квадрат. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его размер и координаты.
  2. Разработать программу с использованием наследования классов, реализующую классы: железнодорожный вагон; вагон для перевозки автомобилей; цистерна. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его вес и количество единиц товара в вагоне.
  3. Разработать программу с использованием наследования классов, реализующую классы: массив; стек; очередь. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран количество элементов и добавьте элемент.
  4. Разработать программу с использованием наследования классов, реализующую классы: воин; пехотинец(винтовка); матрос(кортик). Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его возраст и вид оружия.
  5. Разработать программу с использованием наследования классов, реализующую классы: точка; линия; круг. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран координаты и размер.
  6. Разработать программу с использованием наследования классов, реализующую классы: работник больницы; медсестра; хирург. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран возраст и название должности.
  7. Разработать программу с использованием наследования классов, реализующую классы: точка; квадрат пирамида. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его размер и координаты.
  8. Разработать программу с использованием наследования классов, реализующую классы: реагент; углерод; железо. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран его количество и свойства (форма кристаллической решетки для углерода и чистота выработки руды для железа).
  9. Разработать программу с использованием наследования классов, реализующую классы: работник фирмы; стажер; руководящий сотрудник; директор. Используя виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран целое число - уровень допуска, и название должности.
  10. Разработать программу с использованием наследования классов, реализующую классы: молодой человек; студент; военнослужащий; военный курсант. Используя виртуальное наследование и виртуальные функции, не зная с объектом какого класса вы работаете, выведите на экран сведения о военнообязанности.