Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.96/47: Рейтинг темы: голосов - 47, средняя оценка - 4.96
Строитель
456 / 73 / 4
Регистрация: 18.06.2010
Сообщений: 507
1

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

11.03.2017, 17:30. Показов 9561. Ответов 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
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
11.03.2017, 17:30
Ответы с готовыми решениями:

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

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

Что делает функция compare в коде и зачем она нужна в qsort
Объясните, пожалуйста, что делает функция compare (17 строка) в данном случае и зачем она нужна в...

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

18
Ferrari F1
11.03.2017, 17:33
  #2

Не по теме:

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

0
Эксперт С++
8450 / 3977 / 872
Регистрация: 15.11.2014
Сообщений: 8,949
11.03.2017, 17:42 3
Цитата Сообщение от infobos Посмотреть сообщение
В этой теме хочу новичкам рассказать, как я понял эту виртуальную функцию.
с таким описанием лучше вообще ничего не рассказывать.
1
Mental handicap
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 17:48 4
но вы не разобрали к примеру такой синтаксис
C++
1
    virtual void func1() = 0;
0
Строитель
456 / 73 / 4
Регистрация: 18.06.2010
Сообщений: 507
11.03.2017, 17:50  [ТС] 5
Цитата Сообщение от Azazel-San Посмотреть сообщение
но вы не разобрали к примеру такой синтаксис
Да не разобрал... Просвятите что это?
0
Модератор
Эксперт Python
28772 / 15602 / 3097
Регистрация: 12.02.2012
Сообщений: 25,599
Записей в блоге: 4
11.03.2017, 17:52 6
Цитата Сообщение от infobos Посмотреть сообщение
Просвятите что это?
- просвЕщаю: это называется "чистая виртуальная функция".
0
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 18:03 7
Не происходит никакой подмены, метод родителя как существовал, так продолжает существовать:
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
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 18:10 8
и ни слова об абстрактных классах, кажется вы не доконца поняли что такое виртуальные функции
1
Строитель
456 / 73 / 4
Регистрация: 18.06.2010
Сообщений: 507
11.03.2017, 18:13  [ТС] 9
Цитата Сообщение от Azazel-San Посмотреть сообщение
и ни слова об абстрактных классах, кажется вы не доконца поняли что такое виртуальные функции
Чтобы еще что-то понимать нужно явно получить такую задачу при котором это было бы актуально. Сейчас не стоит задача об абстрактных классах.
Может быть Вы зададите задачу, где нужен будет абстрактный класс. Наверно тогда и все дружно поймем.
0
Mental handicap
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 18:21 10
Цитата Сообщение от infobos Посмотреть сообщение
Сейчас не стоит задача об абстрактных классах.
ем, при изучении виртуальных методов, обычно сразу и дают понять что такое абстрактный класс, они взаимо связаны
Класс, содержащий хотя бы одну чисто виртуальную функцию, считается абстрактным.
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 19:15 11
Накатал тут своё объяснение, надеюсь никого не запутаю ещё сильнее

Виртуальная функция нужна для обеспечения динамического полиморфизма. Если смотреть с высоты абстракций, виртуальные функции позволяют реализовывать общий интерфейс для разных объектов. Допустим, есть слудующая иерархия:
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);
подставляется что-то вроде (на языке псевдоассемблера):
Код
вызвать 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();
компилятор генерирует что-то вроде
Код
взять таблицу виртуальных функций из *p
найти в ней адрес метода draw()
вызвать функцию по этому адресу
В объекте Polygon он вызовет функцию по адресу 0x0db1300, а в объекте Triangle, допустим .0x0db12cd
1
Любитель чаепитий
3552 / 1661 / 510
Регистрация: 24.08.2014
Сообщений: 5,630
Записей в блоге: 1
11.03.2017, 19:24 12
DevAlone, Про виртуальные деструкторы забыли в обоих примерах.
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 19:29 13
Цитата Сообщение от GbaLog- Посмотреть сообщение
DevAlone, Про виртуальные деструкторы забыли в обоих примерах.
Так я их не объяснял) Но вообще да, его нужно всегда объявлять в базовом классе при использовании виртуальных функций, спасибо за замечание.
0
Mental handicap
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 20:36 14
ну по сути создавая виртуальные функции внутри класса, мы делаем его абстрактным, или как можно на него сказать "интерфейс", грубо говоря мы получаем интерфейс, т.е. те ниточки за которые мы можем дергать, вызывая один и тот же метод, но с разной реализацией для каждого отдельного класса наследника (или другими словами полиморфизм, один интерфейс - множество реализаций).
1
331 / 283 / 78
Регистрация: 02.08.2016
Сообщений: 1,008
11.03.2017, 20:41 15
GbaLog-,
Почему после твоего ника здесь стоит +?
Название: 2017-03-11-20:40:12-screenshot.png
Просмотров: 355

Размер: 6.2 Кб
0
Mental handicap
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 20:43 16
К примеру, в моем говнокоде, я попытаюсь это показать:
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
Ferrari F1
11.03.2017, 20:44
  #17

Не по теме:

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

0
Mental handicap
1243 / 621 / 171
Регистрация: 24.11.2015
Сообщений: 2,426
11.03.2017, 20:46 18
Цитата Сообщение от DevAlone Посмотреть сообщение
Почему после твоего ника здесь стоит +?
у меня нету
0
Изображения
 
GbaLog-
11.03.2017, 21:24     Что такое виртуальная функция и зачем она нужна?
  #19

Не по теме:

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

0
11.03.2017, 21:24
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.03.2017, 21:24
Привет! Вот еще темы с ответами:

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.