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

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

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

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

07.04.2011, 18:40. Просмотров 5306. Ответов 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;
Вопрос: в чем смысл использования виртуальных функций если я и обычными методами могу переопределить/изменить базовые функции в классе наследнике???
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.04.2011, 18:40
Здравствуйте! Я подобрал для вас темы с ответами на вопрос виртуальные функции (C++):

виртуальные и чисто виртуальные функции - C++
Чем они отличаются?? если можно, с примерами. И как из виртуальной функции сделать чисто виртуальную? #include <iostream> using...

Виртуальные функции - C++
Составить программу в С Buildere. Объявить базовый класс с именем person {имя, возраст, группа}. Определить функцию show (), которая...

виртуальные функции - C++
интересует 2 вопроса:1)Какими элементами могут отличаться экземпляры одной виртуальной функции, находящиеся в разных производных...

Виртуальные функции - C++
Создать абстрактный базовый класс с виртуальной функцией - объем. Создать производные классы: параллелепипед, пирамида, тетраэдр, шар со...

Виртуальные функции - C++
Разработать программу с использованием наследования классов, реализующую классы: Земноводное(ареал обитания)->Крокодил(род, длина, вес) ...

Виртуальные функции - C++
Объясните, для чего необходимы виртуальные функции. Где их рационально использовать. P.S. Если есть ссылки на толковые материалы - не...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Babulesnik
0 / 0 / 0
Регистрация: 06.01.2011
Сообщений: 21
08.04.2011, 22:25  [ТС] #2
Пожалуйста,помогите кто знает.
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
08.04.2011, 22:29 #3
Сейчас покажу наглядно.
ValeryS
Модератор
6556 / 5022 / 464
Регистрация: 14.02.2011
Сообщений: 16,763
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
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 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
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
08.04.2011, 23:26 #8
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Опишу суть своего кода:
У нас есть абстрактный родительский класс Shape - фигура. В нём объявлена чисто виртуальная функция area - площадь. От этого класса наследуют три дочерних класса - Square, Triangle, Circle - квадрат, треугольник, круг. В каждом из них функция вычисления площади конкретизируется (т.е. пишется реализация, для каждого класса, естественно, своя). Затем, в основной функции, создаётся вектор указателей на Shape - на абстрактный родительский класс. В цикле мы предлагаем пользователю варианты фигур, которые он может создать. Пользователь может создать одну фигуру, может 3, а может 10. Они могут идти в любой последовательности (при каждом запуске разной). Т.е. на этапе компиляции мы ничего не знаем о будущей структуре вектора shapes (какие фигуры, сколько их и в какой они созданы последовательности). В итоге, скажем, пользователь насоздавал 1000 разных фигур. И тут в игру вступает позднее связывание. Поскольку как в родительском классе (без реализации), так и в дочерних (с реализацией) есть функции area, то нам не обязательно каждый раз знать, какая фигура в какой ячейке находится. Нам достаточно в цикле вызывать функцию area для каждой ячейки массива shapes, а уж какая конкретно будет вызвана функция (ведь площадь каждой из трёх фигур вычисляется по-разному) - это будет определено автоматически и нам об этом думать не надо.
Вообще, код не такой уж и большой, основную часть занимают красивости, вывод и т.д. Содержательная часть - три класса и не больше 10 строчек в main.
NightmareZ
1340 / 563 / 37
Регистрация: 31.03.2009
Сообщений: 1,918
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
бжни
2447 / 1652 / 84
Регистрация: 14.05.2009
Сообщений: 7,162
09.04.2011, 00:13 #11
Babulesnik, http://ru.wikipedia.org/wiki/Таблица...альных_методов
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 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
Модератор
6556 / 5022 / 464
Регистрация: 14.02.2011
Сообщений: 16,763
09.04.2011, 07:02 #14
Цитата Сообщение от Babulesnik Посмотреть сообщение
class Shape
{
public:
virtual double area() const = 0;
};
Здесь создаем базовый абстрактный класс с функцией "пустышкой".
"еще одно последнее сказанье"
объект абстрактного класса создать нельзя

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

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

Виртуальные функции - C++
Доброго времени суток. Есть задание. Создать абстрактный класс Function (функция) с виртуальными методами вычисления значения функции...

Виртуальные функции - C++
Пожалуйста, подскажите как быть: class Circles; class Rectangs; class Snake { public: Circles *drr; void start1(); ...

виртуальные функции - C++
ввести виртуальную функцию class A { void init(); } classB:A { void init(); }

Виртуальные функции в С++ - C++
Здравствуйт. Помогите осмыслить доконца вирт. ф-ции. После прочтения источников я собрал определенную картину: Вирт функции...


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

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
09.04.2011, 16:01
Ответ Создать тему
Опции темы

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