Добавил:
Developer Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Магистратура Языки программирования С,C++ / Доклад на тему №8 Наследование, перегрузка, переопределение.docx
Скачиваний:
3
Добавлен:
17.03.2024
Размер:
1.31 Mб
Скачать

Принцип работы

Язык С++ определяет, как должны вести себя виртуальные функции, но реализация этого механизма возложена на разработчика компилятора. Чтобы использовать виртуальные функции, не нужно знать способ реализации, однако изучение принципа работы поможет ориентироваться в программах.

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

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

Если производный класс определяет новую функцию и объявляет ее виртуальной, ее адрес добавляется в таблицу виртуальных функций.

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

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

  • Размер каждого объекта увеличивается на значение, необходимое для хранения адреса.

  • Для каждого класса компилятор создает таблицу (массив) адресов виртуальных функций.

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

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

6.1 Виртуальное наследование

Виртуальное наследование (virtual inheritance) предотвращает появление множественных объектов базового класса в иерархии наследования. Таким образом, конструктор базового класса Device будет вызван только единожды, а обращение к методу turn_on() без его переопределения в дочернем классе не будет вызывать ошибку при компиляции.

Листинг №1.6.1 – Программный код

#include <iostream>

using namespace std;

class Device {

public:

Device() {

cout << "Device constructor called" << endl;

}

void turn_on() {

cout << "Device is on." << endl;

}

};

class Computer : virtual public Device {

public:

Computer() {

cout << "Computer constructor called" << endl;

}

};

class Monitor : virtual public Device {

public:

Monitor() {

cout << "Monitor constructor called" << endl;

}

};

class Laptop : public Computer, public Monitor {};

int main() {

Laptop Laptop_instance;

Laptop_instance.turn_on();

return 0;

}

Примечание: виртуальное наследование в классах Computer и Monitor не разрешит ромбовидное наследование если дочерний класс Laptop будет наследовать класс Device не виртуально ( class Laptop: public Computer, public Monitor, public Device {}; ).