Рубрики

Виртуальные функции и полиморфизм времени выполнения в C ++ | Комплект 1 (Введение)

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

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

Другими словами, виртуальные функции разрешаются поздно, во время выполнения.

#include <iostream>

using namespace std;

  

class Base {

public:

    virtual void show()

    {

        cout << " In Base \n";

    }

};

  

class Derived : public Base {

public:

    void show()

    {

        cout << "In Derived \n";

    }

};

  

int main(void)

{

    Base* bp = new Derived;

  

    // ВРЕМЕННЫЙ ПОЛИМОРФИЗМ

    bp->show();

  

    return 0;

}

Выход:

In Derived

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

Например: рассмотрим программное обеспечение для управления сотрудниками организации.

Пусть в коде есть простой базовый класс Employee , класс содержит виртуальные функции, такие как Повышение уровня () , передача () , повышение () и т. Д. Различные типы сотрудников, такие как Менеджер , Инженер и т. Д., Могут иметь свои собственные реализации виртуальных функций. присутствует в базовом классе Employee.

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

class Employee {

public:

    virtual void raiseSalary()

    {

        / * код повышения зарплаты * /

    }

  

    virtual void promote()

    {

        / * общий промо-код * /

    }

};

  

class Manager : public Employee {

    virtual void raiseSalary()

    {

        / * Управляющий код повышения зарплаты, может содержать

          увеличение специфических стимулов менеджера *

    }

  

    virtual void promote()

    {

        / * Менеджер по продвижению * /

    }

};

  
// Точно так же могут быть другие типы сотрудников

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

void globalRaiseSalary(Employee* emp[], int n)

{

    for (int i = 0; i < n; i++)

  

        // Полиморфный вызов: CallS RaiseSalary ()

        // в соответствии с фактическим объектом, а не

        // в соответствии с типом указателя

        emp[i]->raiseSalary();

}

Как и globalRaiseSalary () , может быть много других операций, которые можно выполнять со списком сотрудников, даже не зная типа экземпляра объекта.
Виртуальные функции настолько полезны, что более поздние языки, такие как Java, сохраняют все методы как виртуальные по умолчанию .

Как компилятор выполняет разрешение во время выполнения?

Для этой цели компилятор поддерживает две вещи:

  1. vtable: таблица указателей на функции, поддерживаемая для каждого класса.
  2. vptr: указатель на таблицу виртуальных, поддерживается на экземпляр объекта (см это для примера).

Компилятор добавляет дополнительный код в двух местах для поддержки и использования vptr .

1) Код в каждом конструкторе. Этот код устанавливает vptr создаваемого объекта. Этот код устанавливает vptr для указания на vtable класса.
2) Код с полиморфным вызовом функции (например, bp-> show () в приведенном выше коде). Где бы ни происходил полиморфный вызов, компилятор вставляет код, чтобы сначала искать vptr, используя указатель или ссылку на базовый класс (в приведенном выше примере, поскольку указанный или ссылочный объект имеет производный тип, доступ к vptr производного класса). После получения vptr можно получить доступ к vtable производного класса. Используя vtable , вызывается адрес производной функции класса show () и вызывается.

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

Викторина о виртуальных функциях .

Ссылки:
http://en.wikipedia.org/wiki/Virtual_method_table
http://en.wikipedia.org/wiki/Virtual_function
http://www.drbio.cornell.edu/pl47/programming/TICPP-2nd-ed-Vol-one-html/Frames.html

Пожалуйста, напишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой теме

Рекомендуемые посты:

Виртуальные функции и полиморфизм времени выполнения в C ++ | Комплект 1 (Введение)

0.00 (0%) 0 votes