Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 39, средняя оценка - 4.79
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
#1

виртуальные функции - C++

07.04.2011, 18:40. Просмотров 5044. Ответов 14
Метки нет (Все метки)

Здравствуйте.Перечитал различные статьи,но не могу до конца вникнуть в смысл использования виртуальных функций.Для взаимного понимания привожу свой код:

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
struct first
{
 Int a,b,c;
   virtual int virt()
    {
            a=b+5;
            return a;
    };
     int not_virt()
    {
             a=b+6;
      return a;
    };
}obj_first;
struct second:first
{
           virtual int virt()
    {
             a=b+7;
      return a;
    };
    int not_virt()
    {
      c=b+8;
      return a;
    }; 
}obj_second;
 
...
 v     =obj_second.virt();
 no_v=obj_second.not_virt();
на выходе соответственно получаю 7 и 8;
Вопрос: в чем смысл использования виртуальных функций если я и обычными методами могу переопределить/изменить базовые функции в классе наследнике???
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
08.04.2011, 22:25  [ТС]     виртуальные функции #2
Пожалуйста,помогите кто знает.
silent_1991
Эксперт С++
4952 / 3028 / 149
Регистрация: 11.11.2009
Сообщений: 7,026
Завершенные тесты: 1
08.04.2011, 22:29     виртуальные функции #3
Сейчас покажу наглядно.
ValeryS
Модератор
6483 / 4949 / 455
Регистрация: 14.02.2011
Сообщений: 16,394
08.04.2011, 22:38     виртуальные функции #4
виртуальные функции это очень интересно
например
C++
1
2
3
4
5
6
7
8
9
10
11
12
class A
{
 fnc();
}
class B:pudlic A
{
fnc();
}
class C:public B
{
fnc();
}
если ты объявишь три объекта а в с
и приведешь их к базовому классу A
например так
C++
1
2
3
4
5
6
7
8
9
A a;
B b;
C c;
A* aa=&a;
A* ab=&b;
A* ac=&c;
aa->fnc();
ab->fnc();
ac->fnc();
то у тебя вызовется функция базового класса
а если объявишь виртуальными
то каждый раз своя
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
08.04.2011, 22:56  [ТС]     виртуальные функции #5
Цитата Сообщение от ValeryS Посмотреть сообщение
виртуальные функции это очень интересно
например
C++
1
2
3
4
5
6
7
8
9
10
11
12
class A
{
 fnc();
}
class B:pudlic A
{
fnc();
}
class C:public B
{
fnc();
}
если ты объявишь три объекта а в с
и приведешь их к базовому классу A
например так
C++
1
2
3
4
5
6
7
8
9
A a;
B b;
C c;
A* aa=&a;
A* ab=&b;
A* ac=&c;
aa->fnc();
ab->fnc();
ac->fnc();
то у тебя вызовется функция базового класса
а если объявишь виртуальными
то каждый раз своя
Спасибо.По вашему примеру с указателями немного понял,но не могу нарисовать полную картину применения виртуальной функции.В букварях пишут,что это основа для динамического изменнения,в статике используются перегрузки функций....Блин все равно не доходит до конца.
silent_1991
Эксперт С++
4952 / 3028 / 149
Регистрация: 11.11.2009
Сообщений: 7,026
Завершенные тесты: 1
08.04.2011, 23:00     виртуальные функции #6
Фух... Код получился больше, чем я предполагал, но он показывает всю мощь динамического связывания и виртуальных функций. Здесь пользователь самостоятельно может задавать последовательность и количество задаваемых фигур, на этапе компиляции неизвестно, в какой ячейке массива shapes какая фигура лежит. Обратите внимание на строку std::cout << shapes[i]->area() << std::endl;. Весь остальной код, по сути, только для красоты, и вся суть в этой строке. Она означает, что, даже не зная на этапе компиляции, какая фигура где находится, мы можем для любой фигуры использовать просто вызов функции area, и автоматически будет вызвана своя функция для каждой фигуры. Полагаю, возникнут вопросы, если так, обращайтесь - растолкую, что не понятно.
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <cmath>
#include <vector>
 
//-----------------------------------------------------------------------------
 
class Shape
{
public:
    virtual double area() const = 0;
};
 
class Square: public Shape
{
public:
    Square(double size = 1.0):
    _side(size)
    {
    }
 
    virtual double area() const
    {
        return _side * _side;
    }
 
private:
    double _side;
};
 
//-----------------------------------------------------------------------------
 
class Triangle: public Shape
{
public:
    Triangle(double side_a = 3.0, double side_b = 4.0, double side_c = 5.0):
    _side_a(side_a),
    _side_b(side_b),
    _side_c(side_c)
    {
    }
 
    virtual double area() const
    {
        double p = (_side_a + _side_b + _side_c) / 2.0;
 
        return sqrt(p * (p - _side_a) * (p - _side_b) * (p - _side_c));
    }
 
private:
    double _side_a;
    double _side_b;
    double _side_c;
};
 
//-----------------------------------------------------------------------------
 
class Circle: public Shape
{
    static const double Pi;
 
public:
    Circle(double radius = 1.0):
    _radius(radius)
    {
    }
 
    virtual double area() const
    {
        return Pi * _radius * _radius;
    }
 
private:
    double _radius;
};
 
const double Circle::Pi = 3.14159265358979323;
 
//-----------------------------------------------------------------------------
 
int main()
{
    setlocale(LC_ALL, "Rus");
 
    std::vector< Shape * > shapes;
    size_t choise;
 
    do
    {
        std::cout << "Меню" << std::endl
                  << "1. Создать квадрат" << std::endl
                  << "2. Создать треугольник" << std::endl
                  << "3. Создать круг" << std::endl
                  << "4. Рассчитать площади созданных фигур и выйти" << std::endl;
        std::cin >> choise;
 
        switch (choise)
        {
        case 1:
            {
                double side;
 
                std::cout << "Квадрат:" << std::endl;
                std::cout << "Введите длину стороны: ";
                std::cin >> side;
 
                shapes.push_back(new Square (side));
 
                break;
            }
 
        case 2:
            {
                double side_a, side_b, side_c;
 
                std::cout << "Треугольник:" << std::endl;
                std::cout << "Введите длину стороны a: ";
                std::cin >> side_a;
                std::cout << "Введите длину стороны b: ";
                std::cin >> side_b;
                std::cout << "Введите длину стороны c: ";
                std::cin >> side_c;
 
                shapes.push_back(new Triangle (side_a, side_b, side_c));
 
                break;
            }
 
        case 3:
            {
                double radius;
 
                std::cout << "Круг:" << std::endl;
                std::cout << "Введите длину радиуса: ";
                std::cin >> radius;
 
                shapes.push_back(new Circle (radius));
 
                break;
            }
 
        case 4:
            {
                Square *square_pointer;
                Triangle *triangle_pointer;
                Circle *circle_pointer;
                size_t size = shapes.size();
 
                for (size_t i = 0; i < size; ++i)
                {
                    square_pointer = dynamic_cast< Square * >(shapes[i]);
                    triangle_pointer = dynamic_cast< Triangle * >(shapes[i]);
                    circle_pointer = dynamic_cast< Circle * >(shapes[i]);
 
                    if (square_pointer != 0)
                        std::cout << "Квадрат.     Площадь = ";
 
                    if (triangle_pointer != 0)
                        std::cout << "Треугольник. Площадь = ";
 
                    if (circle_pointer != 0)
                        std::cout << "Круг.        Площадь = ";
 
                    std::cout << shapes[i]->area() << std::endl;
                }
 
                break;
            }
 
        default:
            {
                std::cout << "Недопустимое действие! Попробуйте снова." << std::endl;
 
                break;
            }
        };
    }
    while (choise != 4);
 
    size_t size = shapes.size();
 
    for (size_t i = 0; i < size; ++i)
        delete shapes[i];
 
    return 0;
}
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
08.04.2011, 23:18  [ТС]     виртуальные функции #7
Цитата Сообщение от silent_1991 Посмотреть сообщение
Фух... Код получился больше, чем я предполагал, но он показывает всю мощь динамического связывания и виртуальных функций. Здесь пользователь самостоятельно может задавать последовательность и количество задаваемых фигур, на этапе компиляции неизвестно, в какой ячейке массива shapes какая фигура лежит. Обратите внимание на строку std::cout << shapes[i]->area() << std::endl;. Весь остальной код, по сути, только для красоты, и вся суть в этой строке. Она означает, что, даже не зная на этапе компиляции, какая фигура где находится, мы можем для любой фигуры использовать просто вызов функции area, и автоматически будет вызвана своя функция для каждой фигуры. Полагаю, возникнут вопросы, если так, обращайтесь - растолкую, что не понятно.
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <cmath>
#include <vector>
 
//-----------------------------------------------------------------------------
 
class Shape
{
public:
    virtual double area() const = 0;
};
 
class Square: public Shape
{
public:
    Square(double size = 1.0):
    _side(size)
    {
    }
 
    virtual double area() const
    {
        return _side * _side;
    }
 
private:
    double _side;
};
 
//-----------------------------------------------------------------------------
 
class Triangle: public Shape
{
public:
    Triangle(double side_a = 3.0, double side_b = 4.0, double side_c = 5.0):
    _side_a(side_a),
    _side_b(side_b),
    _side_c(side_c)
    {
    }
 
    virtual double area() const
    {
        double p = (_side_a + _side_b + _side_c) / 2.0;
 
        return sqrt(p * (p - _side_a) * (p - _side_b) * (p - _side_c));
    }
 
private:
    double _side_a;
    double _side_b;
    double _side_c;
};
 
//-----------------------------------------------------------------------------
 
class Circle: public Shape
{
    static const double Pi;
 
public:
    Circle(double radius = 1.0):
    _radius(radius)
    {
    }
 
    virtual double area() const
    {
        return Pi * _radius * _radius;
    }
 
private:
    double _radius;
};
 
const double Circle::Pi = 3.14159265358979323;
 
//-----------------------------------------------------------------------------
 
int main()
{
    setlocale(LC_ALL, "Rus");
 
    std::vector< Shape * > shapes;
    size_t choise;
 
    do
    {
        std::cout << "Меню" << std::endl
                  << "1. Создать квадрат" << std::endl
                  << "2. Создать треугольник" << std::endl
                  << "3. Создать круг" << std::endl
                  << "4. Рассчитать площади созданных фигур и выйти" << std::endl;
        std::cin >> choise;
 
        switch (choise)
        {
        case 1:
            {
                double side;
 
                std::cout << "Квадрат:" << std::endl;
                std::cout << "Введите длину стороны: ";
                std::cin >> side;
 
                shapes.push_back(new Square (side));
 
                break;
            }
 
        case 2:
            {
                double side_a, side_b, side_c;
 
                std::cout << "Треугольник:" << std::endl;
                std::cout << "Введите длину стороны a: ";
                std::cin >> side_a;
                std::cout << "Введите длину стороны b: ";
                std::cin >> side_b;
                std::cout << "Введите длину стороны c: ";
                std::cin >> side_c;
 
                shapes.push_back(new Triangle (side_a, side_b, side_c));
 
                break;
            }
 
        case 3:
            {
                double radius;
 
                std::cout << "Круг:" << std::endl;
                std::cout << "Введите длину радиуса: ";
                std::cin >> radius;
 
                shapes.push_back(new Circle (radius));
 
                break;
            }
 
        case 4:
            {
                Square *square_pointer;
                Triangle *triangle_pointer;
                Circle *circle_pointer;
                size_t size = shapes.size();
 
                for (size_t i = 0; i < size; ++i)
                {
                    square_pointer = dynamic_cast< Square * >(shapes[i]);
                    triangle_pointer = dynamic_cast< Triangle * >(shapes[i]);
                    circle_pointer = dynamic_cast< Circle * >(shapes[i]);
 
                    if (square_pointer != 0)
                        std::cout << "Квадрат.     Площадь = ";
 
                    if (triangle_pointer != 0)
                        std::cout << "Треугольник. Площадь = ";
 
                    if (circle_pointer != 0)
                        std::cout << "Круг.        Площадь = ";
 
                    std::cout << shapes[i]->area() << std::endl;
                }
 
                break;
            }
 
        default:
            {
                std::cout << "Недопустимое действие! Попробуйте снова." << std::endl;
 
                break;
            }
        };
    }
    while (choise != 4);
 
    size_t size = shapes.size();
 
    for (size_t i = 0; i < size; ++i)
        delete shapes[i];
 
    return 0;
}
Спасибо большое,но код слишком объемный.Можно пояснить "даже не зная на этапе компиляции, какая фигура где находится, мы можем для любой фигуры использовать просто вызов функции area, и автоматически будет вызвана своя функция для каждой фигуры" ? Или еще лучше показать пример на моем коде,что нельзя сделать без использования виртуальных функций.
silent_1991
Эксперт С++
4952 / 3028 / 149
Регистрация: 11.11.2009
Сообщений: 7,026
Завершенные тесты: 1
08.04.2011, 23:26     виртуальные функции #8
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Опишу суть своего кода:
У нас есть абстрактный родительский класс Shape - фигура. В нём объявлена чисто виртуальная функция area - площадь. От этого класса наследуют три дочерних класса - Square, Triangle, Circle - квадрат, треугольник, круг. В каждом из них функция вычисления площади конкретизируется (т.е. пишется реализация, для каждого класса, естественно, своя). Затем, в основной функции, создаётся вектор указателей на Shape - на абстрактный родительский класс. В цикле мы предлагаем пользователю варианты фигур, которые он может создать. Пользователь может создать одну фигуру, может 3, а может 10. Они могут идти в любой последовательности (при каждом запуске разной). Т.е. на этапе компиляции мы ничего не знаем о будущей структуре вектора shapes (какие фигуры, сколько их и в какой они созданы последовательности). В итоге, скажем, пользователь насоздавал 1000 разных фигур. И тут в игру вступает позднее связывание. Поскольку как в родительском классе (без реализации), так и в дочерних (с реализацией) есть функции area, то нам не обязательно каждый раз знать, какая фигура в какой ячейке находится. Нам достаточно в цикле вызывать функцию area для каждой ячейки массива shapes, а уж какая конкретно будет вызвана функция (ведь площадь каждой из трёх фигур вычисляется по-разному) - это будет определено автоматически и нам об этом думать не надо.
Вообще, код не такой уж и большой, основную часть занимают красивости, вывод и т.д. Содержательная часть - три класса и не больше 10 строчек в main.
NightmareZ
1339 / 562 / 37
Регистрация: 31.03.2009
Сообщений: 1,916
08.04.2011, 23:29     виртуальные функции #9
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Цитата Сообщение от Babulesnik Посмотреть сообщение
пример на моем коде
Твой код похож на безумие. Он заставляет меня плакать.

Цитата Сообщение от Babulesnik Посмотреть сообщение
Можно пояснить "даже не зная на этапе компиляции, какая фигура где находится, мы можем для любой фигуры использовать просто вызов функции area, и автоматически будет вызвана своя функция для каждой фигуры"?
Динамический полиморфизм, реализуемый при помощи наследования и виртуальных функций, позволяет вызывать функции, реализованные в наследниках через указатель или ссылку на базовый класс.

Есть, допустим, у тебя класс Фигура. В нём есть абстрактная функция Отрисовать. Фигура не умеет себя рисовать, потому что понятия не имеет, что она за фигура такая.

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

Теперь у тебя есть задача: создать кучу объектов этих классов и рисовать их. Как это сделать? Создаёшь массив указателей на объекты базового класса, т.е. класса Фигура. Любой указатель на объект класса-наследника ты можешь привести к указателю на объект базового класса, а потому можешь с помощью new создавать объекты классов Квадрат, Круг, Треугольник и помещать их в этот массив.

Если теперь пройтись по всем элементам массива, то вызывая функцию базового класса Отрисовать, реально будет вызываться функция наследника. То есть в этом массиве ты хранишь много-много разных фигур, говоришь им Отрисовать, а каждая из них сама рисует себя как нужно.
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
09.04.2011, 00:08  [ТС]     виртуальные функции #10
Спасибо огромное silent_1991 и NightmareZ за столь детальное разъяснение.Хочу задать пару вопросов по коду:
class Shape
{
public:
virtual double area() const = 0;
};
Здесь создаем базовый абстрактный класс с функцией "пустышкой".

std::vector< Shape * > shapes;
Список указателей на объекты наследники

shapes.push_back(new Square (side));
Добавляем к списку созданную фигуру

std::cout << shapes[i]->area() << std::endl;
Собственно вот и главный вопрос,как он узнает какую функцию ему выбрать?Тоесть к какой фигуре относится соответствующая функция.
alex_x_x
бжни
2443 / 1648 / 84
Регистрация: 14.05.2009
Сообщений: 7,163
09.04.2011, 00:13     виртуальные функции #11
Babulesnik, http://ru.wikipedia.org/wiki/Таблица...альных_методов
silent_1991
Эксперт С++
4952 / 3028 / 149
Регистрация: 11.11.2009
Сообщений: 7,026
Завершенные тесты: 1
09.04.2011, 00:25     виртуальные функции #12
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Babulesnik, так ведь в том и суть виртуальных функций! Что мы не обязаны знать, к какой фигуре какая функция относится, главное, что программа может это узнать.
На самом деле реализация виртуальных функций не регламентирована стандартом, регламентировано только поведение, но обычно виртуальные функции реализуются через указатели на функции. В каждом экземпляре класса расположен указатель на таблицу указателей на функции (так называемая vtable) класса, и при вызове виртуального метода через указатель на базовый класс, на самом деле мы ведь попадаем в экземпляр того класса, на который фактически указывает этот указатель (т.е. на экземпляр некоторого дочернего класса), и здесь натыкаемся на vtable, в которой расположены указатели уже на конкретные функции соответствующих классов.
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
09.04.2011, 00:42  [ТС]     виртуальные функции #13
Всем большое спасибо,вроде дошло )))
ValeryS
Модератор
6483 / 4949 / 455
Регистрация: 14.02.2011
Сообщений: 16,394
09.04.2011, 07:02     виртуальные функции #14
Цитата Сообщение от Babulesnik Посмотреть сообщение
class Shape
{
public:
virtual double area() const = 0;
};
Здесь создаем базовый абстрактный класс с функцией "пустышкой".
"еще одно последнее сказанье"
объект абстрактного класса создать нельзя

попробуй создай простейшую программу с отладочным кодом(Dedug)
и дизасемблируй ее(лучше IDA)
и увидишь как реализуется наследование, виртуальные функции.......
большинство вопросов отпадет
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.04.2011, 16:01     виртуальные функции
Еще ссылки по теме:

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

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

Или воспользуйтесь поиском по форуму:
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
09.04.2011, 16:01  [ТС]     виртуальные функции #15
Цитата Сообщение от ValeryS Посмотреть сообщение
"еще одно последнее сказанье"
объект абстрактного класса создать нельзя

попробуй создай простейшую программу с отладочным кодом(Dedug)
и дизасемблируй ее(лучше IDA)
и увидишь как реализуется наследование, виртуальные функции.......
большинство вопросов отпадет
Спасибо,попробую.
Yandex
Объявления
09.04.2011, 16:01     виртуальные функции
Ответ Создать тему
Опции темы

Текущее время: 00:35. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru