Другое

Виртуальные методы и функции в языке С: определение, таблица и примеры

Lorem ipsum dolor

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

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

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

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

Виртуальные функции в С обозначаются специальным словом «virtual». Давайте рассмотрим простейший пример, как используются виртуальные методы в С:

class Example1

{

public:

    Example1() {number = 100;}

    virtual void set()

    {

        std::cout << "Example1 отлично растет" << std::endl;

        number++;

    }

    int get()

    {

        set();

        return number;

    }

 

protected:

    int number;

};

 

class Example2 : public Example1

{

public:

    void set()

    {

        std::cout << " Example2 быстро убывает" << std::endl;

        number--;

    }

};

 

Чуть выше мы привели простой пример, где располагаем классом «Example1», у которого есть собственные методы «get()» и «set()». Метод «гет» мы помечаем как виртуальную функцию, поэтому в классе «Example2» он будет реализован по-своему. Для того чтобы наследующий класс «Example2» имел доступ к целочисленному значению «number», мы помечаем это значение специальным термином «protected».

Таким образом, если мы пропишем так:

int main()

{

    Example1 example1;

    Example2 example2;

 

    std::cout << "Значение Example1: " << example1.get() << std::endl;

    std::cout << "Значение Example2: " <<example2.get() << std::endl;

 

    return 0;

}

 

Тогда получим следующий результат:

Example1 отлично растет

Значение Example1: 101

Example2 быстро убывает

Example2: 99

Как видно из примера, виртуальные функции в С реализовать несложно. Редко будет так, что в программе будет применяться только одна виртуальная функция, обычно их применяется определенное множество. Чтобы «не запутаться» в реализации виртуальных функций, их заносят в специальную таблицу «virtable».

Таблица виртуальных методов в С

Сейчас мы рассмотрим, как реализовать виртуальные функции С в таблице виртуальных функций «virtable». За основу возьмем наш класс Example1.

/* Для начала объявляем создание структуры на основе нашего класса */

structure Example1;

 

/* Создаем таблицу функций, где будут храниться указатели на функции. */

typedef structure {

    void (*Example1)(structure Example1*); /* создается подобие "конструктора" */

    void (*set)(structure Example1*); /* конструктор функции set */

    int (*get)(structure Example1*); /* конструктор функции get */

} Example1_functiontable;

 

typedef structure Example1 {

    int number;

    Example1_functiontable *virtable; /* Таблица виртуальных методов нашего класса Example1 */

} Example1;

 

/* Создаем прототипы функций класса Example1 */

void Example1_constructureor(Example1 *this);

void Example1_set(Example1 *this);

int Example1_get(Example1 *this);

 

/* Инициализируем таблицу виртуальных методов класса Example1 */

Example1_functiontable Example1_virtable = {Example1_constructureor,

                                  Example1_set,

                                  Example1_get };

 

/* Реализуем виртуальные функции класса Example1 */

void Example1_constructureor(Example1 *this) {

    this->virtable = &Example1_virtable;

    this->number = 100;

}

 

void Example_set(Example1*this) {

    printf("Example1 отлично растет\n");

    this->number++;

}

 

int Example1_get(Example1*this) {

    this->virtable->set(this);

    return this->number;

}

 

Если внимательно рассмотреть вышеописанный код, тогда можно заметить, что «Example1_get()» осуществляет вызов «set()», применяя указатель из «virtable». Самое время посмотреть, как реализуется наследующий класс:

/* Для начала объявляем создание структуры на основе класса-наследника */

structure Example2;

 

         typedef structure {

    void (*Example2)(structure Example2*);

    void (*set)(structure Example2*);

    void (*get)(structure Example1*);

} Example2_functiontable;

 

typedef structure Example2 {

   Example1 inherited_class;

} Example2;

 

void Example2_constructureor(Example2 *this);

void Example2_set(Example2 *this);

int Example2_get(Example2 *this);

 

Example2_functiontable Example2_virtable = {Example2_constructureor, Example2_set, Example2_get};

 

void Example2_constructureor(Example2 *this) {

    Example1_constructureor((Example1*)this);

 

    /*Необходимо явное приведение типов для создания таблицы виртуальных методов*/

    this->inherited_class.virtable = (Example1_functiontable*)&Example2_virtable;

}

 

void Example2_set(Example2 *this) {

    printf("Example2 быстро убывает\n");

    this->inherited_class.number--;

}

 

int Example2_get(Example2 *this) {

    this->inherited_class.virtable->set((Example1*)this);

    return this->inherited_class.number;

}

 

В коде выше тоже видно, что функция «set()» использует указатели из «virtable» из реализации «get()» класса Example2. Осталось только посмотреть, как теперь будет представлена функция «main()»:

int main() {

    Example1 example1;

    Example2 example2;

 

    Example1_constructureor(&example1);

    Example2_constructureor(&example2);

    printf("Значение Example1: %d\n", example1.virtable->get(&example1));

 

    /* Идет обращение к get() через класс родителя */

    printf("Значение Example2: %d\n",

              example2.inherited_class.virtable->get((structure Example1*)&example2));

}

 

Такая программа нам выведет следующее:

Еxample1 отлично растет

Значение Еxample1: 101

Еxample2 быстро убывает

Значение Еxample2: 99

Заключение

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

Схожие статьи

Общие принципы разработки ПО и их основоположники. Коротко о главном
Другое

Общие принципы разработки ПО и их основоположники. Коротко о главном

Интерфейс Adfox: первое знакомство с возможностями интерфейса
Другое

Интерфейс Adfox: первое знакомство с возможностями интерфейса

Трассировка лучей в компьютерной графике
Другое

Трассировка лучей в компьютерной графике

Антипаттерны программирования. Какая польза и есть ли она вообще?
Другое

Антипаттерны программирования. Какая польза и есть ли она вообще?