Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.81/88: Рейтинг темы: голосов - 88, средняя оценка - 4.81
Строитель
 Аватар для infobos
460 / 77 / 4
Регистрация: 18.06.2010
Сообщений: 514

Что такое виртуальная функция и зачем она нужна?

11.03.2017, 17:30. Показов 18244. Ответов 18
Метки нет (Все метки)

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

Виртуальная функция - это по сути тоже самое, что метод, процедура и т.д. Но этот метод может не запускаться. То есть вы вызываете метод, а он тупо не работает. За место него работает другой метод. Вы создаете виртуальную функцию, которая по названию совпадает с другим методом, находящимся в другом производном классе. То есть у Вас есть два одинаковых метода имеющие разный алгоритм действия. И программно в коде идет замена методов.
Этот метод превращается в виртуальнуй, именно в классе(class). Когда Вы создаете класс и внутри класса создаете виртуальные методы.
virtual void func1()

А зачем нужны виртуальные методы(функции и процедуры)?

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

Виртуальный метод становится полезным только тогда, когда нужно программно один и тот же метод менять по некоторому алгоритму в программном коде.

Также чтобы сделать это потребуется создать класс + производный класс. И в двух классах сделать два одинаковых метода(функции). Один метод обозначить как виртуальный (virtual void func1())

Чтобы понять просто посмотрите на код с++ и станет понятно:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>//Библиотека ввода вывода
 
class classA
{
public:
    virtual void func1()
    {
        std::cout << "class classA, func1()\n";
    }
 
    void func2()
    {
        std::cout << "class classA, func2()\n";
    }
};
 
 
class classB: public classA
{
public:
    void func1()
    {
        std::cout << "class classB, func1()\n";
    }
 
    void func2()
    {
        std::cout << "class classB, func2()\n";
    }
};
 
 
void main()
{
 
    setlocale(LC_ALL, "rus");
 
 
    std::cout << "\n";
    std::cout << "Новый тест 1\n";
 
 
 
    classA *ukazatel;
    classA a;
    classB b;
 
    ukazatel=&a;//Указатель на класс A
 
    ukazatel->func1();//сработает: класс A func1()
    ukazatel->func2();//сработает: класс A func2()
 
    ukazatel=&b;//Указатель на класс B
 
    ukazatel->func1();//сработает: класс B func1(), потому что он был объявлен виртуальным!
    ukazatel->func2();//сработает: класс A func2(), потому что он не был объявлен виртуальным!
 
 
    int s3;
    std::cin>>s3;
 
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
11.03.2017, 17:30
Ответы с готовыми решениями:

Что такое рекурсия? Зачем она нужна?
Объясните пож человеческим языком, что такое Рекурсия. Я знаю что это вызов функции самой себя. Но всё равно не могу догнать зачем...

Что такое тестирующая программа и зачем она нужна?
Есть задание, Написать функцию для перевода переменной типа long в символьную строку в двоичном представлении ( ltoab( long num, char s)...

Что делает функция compare в коде и зачем она нужна в qsort
Объясните, пожалуйста, что делает функция compare (17 строка) в данном случае и зачем она нужна в qsort? #include &lt;stdio.h&gt; ...

18
11.03.2017, 17:33

Не по теме:

infobos, странная у вас подпись под ником)

0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
11.03.2017, 17:42
Цитата Сообщение от infobos Посмотреть сообщение
В этой теме хочу новичкам рассказать, как я понял эту виртуальную функцию.
с таким описанием лучше вообще ничего не рассказывать.
1
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 17:48
но вы не разобрали к примеру такой синтаксис
C++
1
    virtual void func1() = 0;
0
Строитель
 Аватар для infobos
460 / 77 / 4
Регистрация: 18.06.2010
Сообщений: 514
11.03.2017, 17:50  [ТС]
Цитата Сообщение от Azazel-San Посмотреть сообщение
но вы не разобрали к примеру такой синтаксис
Да не разобрал... Просвятите что это?
0
Супер-модератор
Эксперт функциональных языков программированияЭксперт Python
 Аватар для Catstail
38173 / 21108 / 4307
Регистрация: 12.02.2012
Сообщений: 34,704
Записей в блоге: 14
11.03.2017, 17:52
Цитата Сообщение от infobos Посмотреть сообщение
Просвятите что это?
- просвЕщаю: это называется "чистая виртуальная функция".
0
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 18:03
Не происходит никакой подмены, метод родителя как существовал, так продолжает существовать:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
 
using std::cout;
using std::cin;
using std::endl;
 
class Parent
{
public:
 
    virtual void doSomething()
    {
        cout << "parent is doing something..." << endl;
    }
};
class Child : public Parent
{
public:
 
    void doSomething()
    {
        Parent::doSomething();
        cout << "child is doing something..." << endl;
    }
};
 
int main()
{
 
    Parent parent;
    Child child;
 
    Parent *pparent = &child;
    pparent->doSomething();
 
 
 
    return 0;
}
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 18:10
и ни слова об абстрактных классах, кажется вы не доконца поняли что такое виртуальные функции
1
Строитель
 Аватар для infobos
460 / 77 / 4
Регистрация: 18.06.2010
Сообщений: 514
11.03.2017, 18:13  [ТС]
Цитата Сообщение от Azazel-San Посмотреть сообщение
и ни слова об абстрактных классах, кажется вы не доконца поняли что такое виртуальные функции
Чтобы еще что-то понимать нужно явно получить такую задачу при котором это было бы актуально. Сейчас не стоит задача об абстрактных классах.
Может быть Вы зададите задачу, где нужен будет абстрактный класс. Наверно тогда и все дружно поймем.
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 18:21
Цитата Сообщение от infobos Посмотреть сообщение
Сейчас не стоит задача об абстрактных классах.
ем, при изучении виртуальных методов, обычно сразу и дают понять что такое абстрактный класс, они взаимо связаны
Класс, содержащий хотя бы одну чисто виртуальную функцию, считается абстрактным.
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 19:15
Накатал тут своё объяснение, надеюсь никого не запутаю ещё сильнее

Виртуальная функция нужна для обеспечения динамического полиморфизма. Если смотреть с высоты абстракций, виртуальные функции позволяют реализовывать общий интерфейс для разных объектов. Допустим, есть слудующая иерархия:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class AbstractFigure
{
public:
    virtual void draw(Picture &pic, Point pos) = 0;
};
 
class Triangle : public AbstractFigure
{
    /* ... */
public:
    // здесь ключевое слово virtual можно опускать
    virtual void draw(Picture &pic, Point pos)
    {
        pic.drawLine(pos + p1, pos + p2);
        pic.drawLine(pos + p2, pos + p3);
        pic.drawLine(pos + p3, pos + p1);
    }
};
 
class Polygon : public AbstractFigure
{
    /* ... */
public:
    // здесь ключевое слово virtual можно опускать
    virtual void draw(Picture &pic, Point pos)
    {
        if(points.size() < 3)
            throw std::exception();
        
        for(int i = 1; i < points.size(); i++) {
            pic.drawLine(points[i-1], points[i]);
        }
        pic.drawLine(points[points.size() - 1], points[0]);
    }
};
AbstractFigure предоставляет интерфейс, который позволяет фигуре нарисовать себя на картинке в нужном месте, при этом благодаря = 0 после сигнатуры(по умному чистая виртуальная функция) AbstractFigure становится абстрактным классом и создать его экземпляр невозможно. Таким образом, имея массив указателей:
C++
1
AbstractFigure* array[SIZE];
можно отобразить все фигуры на картинке с помощью простого цикла:
C++
1
2
for(int i = 0; i < SIZE; i++)
        array[i]->draw(picture, Point(i*10, i*20));
При этом указатели могут указывать на объект любого класса, унаследованного от AbstractFigure, будь то Triangle, Polygon, FilledTriangle или любой другой. И когда в программу нужно будет добавить ещё одну фигуру, допустим проекцию четырёхмерного куба на двумерное пространство, мы просто добавим ещё один класс с парой полей и одним методом draw.

Если попытаться разобраться, как это всё работает, то без virtual оно и не должно работать. Если убрать все virtual из вышеприведённого кода и добавить реализацию функции в AbstractFigure, допустим так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class AbstractFigure
{
public:
    void draw(Picture &pic, Point pos)
    {
        // do nothing
    }
};
 
class Triangle : public AbstractFigure
{
    /* ... */
public:
    void draw(Picture &pic, Point pos)
    {
        pic.drawLine(pos + p1, pos + p2);
        pic.drawLine(pos + p2, pos + p3);
        pic.drawLine(pos + p3, pos + p1);
    }
};
 
class Polygon : public AbstractFigure
{
    /* ... */
public:
    void draw(Picture &pic, Point pos)
    {
        if(points.size() < 3)
            throw std::exception();
 
        for(int i = 1; i < points.size(); i++) {
            pic.drawLine(points[i-1], points[i]);
        }
        pic.drawLine(points[points.size() - 1], points[0]);
    }
};
то код будет работать и иногда даже правильно, до тех пор, пока мы не используем указатели. Если создать объект AbstractFigure, то он будет содержать только свой метод draw (ну ещё стандартный конструктор, оператор присваивания и прочее, но это сейчас не важно)
C++
1
2
AbstractFigure figure;
figure.draw(picture, 0, 0);
и при вызове draw он будет делать ничего, как и должен.
Если создать объект Triangle, он будет содержать все методы и поля AbstractFigure, в нашем случае метод draw и все свои методы и поля, поскольку у методов одинаковое название, Triangle скрывает метод базового класса и в следующем коде вызывается метод, рисующий треугольник:
C++
1
2
Triangle triangle;
triangle.draw(picture, 0, 0);
Компилятор знает, что у Triangle есть свой метод draw и на этапе компиляции вместо
C++
1
triangle.draw(picture, 0, 0);
подставляется что-то вроде (на языке псевдоассемблера):
Code
1
вызвать 0x0db12cd
где 0x0db12cd - адрес функции draw из класса Triangle, функция draw из AbstractFigure продолжает существовать в памяти и её по прежнему можно вызвать.
А теперь добавим немного указателей, указатель - это просто ячейка памяти(обычно 64 бита), которая указывает на другую ячейку:
C++
1
AbstractFigure * p = new AbstractFigure;
Здесь p указывает на ячейку в памяти, где расположен объект AbstractFigure и когда мы вызываем draw
C++
1
2
AbstractFigure * p = new AbstractFigure;
p->draw(picture, 0, 0);
компилятор вызывает функцию draw из AbstractFigure, он ориентируется только на указатель, откуда ему знать, что мы в этот указатель положим. Также для указателя на Triangle, компилятор будет генерировать вызов функции draw из Triangle.
C++
1
2
Triangle * p = new Triangle;
p->draw(picture, 0, 0);
Но таким образом полиморфизма не добьёшься и придётся создавать по массиву для каждого типа объектов, либо извращаться с небезопасным приведением типов. Поэтому умные люди сделали так, чтобы указатель на объект базового класса мог указывать на объекты производных классов, т.е. разрешили следующий код:
C++
1
AbstractFigure * p = new Triangle;
Но компилятор по прежнему оринтируется на то, что знает, а знает он, что указатель указывает на объект AbstractFigure или на объект производного от него класса и что у этого нечто точно есть метод draw из AbstractFigure. Поэтому следующий код вызывает метод draw из AbstractFigure.
C++
1
2
AbstractFigure * p = new Triangle;
p->draw(picture, 0, 0);
И вот тут как раз и нужна виртуальная функция, поскольку указатель - это просто ячейка, которая может меняться, компилятор не вправе полагать, что между
C++
1
AbstractFigure * p = new Triangle;
и
C++
1
p->draw();
не встретится
C++
1
AbstractFigure * p = new Polygon;
До времени выполнения неизвестно, какая будет вызвана функция, а чтобы было известно, нужно поместить во все объекты, унаследованные от AbstractFigure, информацию о том, какую функцию нужно вызвать, такой информацией является таблица виртуальных функий, где записано, какую функцию нужно вызвать для данного объекта и тогда, при виде
C++
1
p->draw();
компилятор генерирует что-то вроде
Code
1
2
3
взять таблицу виртуальных функций из *p
найти в ней адрес метода draw()
вызвать функцию по этому адресу
В объекте Polygon он вызовет функцию по адресу 0x0db1300, а в объекте Triangle, допустим .0x0db12cd
1
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
11.03.2017, 19:24
DevAlone, Про виртуальные деструкторы забыли в обоих примерах.
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 19:29
Цитата Сообщение от GbaLog- Посмотреть сообщение
DevAlone, Про виртуальные деструкторы забыли в обоих примерах.
Так я их не объяснял) Но вообще да, его нужно всегда объявлять в базовом классе при использовании виртуальных функций, спасибо за замечание.
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 20:36
ну по сути создавая виртуальные функции внутри класса, мы делаем его абстрактным, или как можно на него сказать "интерфейс", грубо говоря мы получаем интерфейс, т.е. те ниточки за которые мы можем дергать, вызывая один и тот же метод, но с разной реализацией для каждого отдельного класса наследника (или другими словами полиморфизм, один интерфейс - множество реализаций).
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 20:41
GbaLog-,
Почему после твоего ника здесь стоит +?
Название: 2017-03-11-20:40:12-screenshot.png
Просмотров: 581

Размер: 6.2 Кб
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 20:43
К примеру, в моем говнокоде, я попытаюсь это показать:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>  
#include <ctime>
using namespace std;
 
class Unit {
public:
    virtual void print() = 0;
};
 
class Dragon : public Unit {
public:
    void print() {
        cout << "I'm a dragon" << endl;
    }
};
 
class Troll : public Unit {
public:
    void print() {
        cout << "I'm a troll" << endl;
    }
};
 
Unit *genRandomUnit() {
    int a = rand() % 2;
    if (a == 0)
        return new Troll;
    else 
        return new Dragon;
}
 
int main() {
    
    Unit *p[10];
 
    srand(time(NULL));
    for (int i = 0; i < 10; i++) {
        p[i] = genRandomUnit();
        p[i]->print();
    }
 
    return 0;
}
1
11.03.2017, 20:44

Не по теме:

DevAlone, он типо у тебя в друзьяшках, а рядом с друзьями + ставится, насколько я понял

0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
11.03.2017, 20:46
Цитата Сообщение от DevAlone Посмотреть сообщение
Почему после твоего ника здесь стоит +?
у меня нету
Изображения
 
0
11.03.2017, 21:24

Не по теме:

Цитата Сообщение от DevAlone Посмотреть сообщение
Почему после твоего ника здесь стоит +?
Из-за того, что я у вас в друзьях. :)

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
11.03.2017, 21:24
Помогаю со студенческими работами здесь

Что такое виртуальная функция и абстрактный класс?
здравствуйте товарищи программисты может кто то может объяснить что такое виртуальная фукция и что такое абстрактный класс ...

Зачем нужна поддержка SLI/CrossFire и что она дает?
Я выбираю материнскую плату и вот нашел: ASRock M3N78D но там нет Поддержка SLI/CrossFire? и вот я хотел спросить зачем нужен...

Напишите простенько что такое фабрика и где она нужна
Привет, друзья! Напишите, пожалуйста, простенько, поверхностно, зачем нужна фабрика класса? Добавлено через 14 минут Чем он от...

IPEndPoint - Что такое локальная конечная точка и для чего она нужна
Что такое локальная конечная точка и для чего она нужна. Если можно объясните как можно проще. Спасибо. newbiecoderr, не дублируйте...

Что за функция, нужна ли она вообще?!
помогите пожалуйста разобраться (что и зачем &quot;try&quot; и &quot;except&quot;), взял исходник посмотреть, но не понимаю что здесь происходит :( try...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru