Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
9 / 9 / 5
Регистрация: 15.11.2011
Сообщений: 114
1

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

13.10.2017, 10:39. Показов 1254. Ответов 16
Метки нет (Все метки)

Имеется базовый класс Shape:
C++
1
2
3
4
5
class Shape
{
protected:
    virtual void drawShape() { }
}
и производные классы Circle, Rectangle и т.д. Естественно, код функции drawShape() для каждого производного класса будет разный. Есть еще одна функция drawScalingShape(). В принципе код этой функции для всех производных классов одинаковый:
C++
1
2
3
4
void drawScalingShape()
{
    scale(drawShape());
}
Вопрос: можно ли определить эту функцию в базовом классе? (чтобы не переписывать ее в каждом производном)
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.10.2017, 10:39
Ответы с готовыми решениями:

Виртуальные и чисто виртуальные функции
Чем они отличаются?? если можно, с примерами. И как из виртуальной функции сделать чисто...

виртуальные функции
Помогите пожалуйста, создать абстрактный класс Shape с двумя чисто виртуальными методами Area() и...

Виртуальные функции.
Приветствую всех. Дана такая программа (на самом деле она больше и сложнее, но структура и проблема...

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

__________________

Записывайтесь на профессиональные курсы C++ разработчиков
16
2851 / 1715 / 352
Регистрация: 09.09.2017
Сообщений: 7,226
13.10.2017, 10:51 2
Для этого наследование и придумали, чтобы не переписывать общие методы родительского класса в каждом потомке.
P.S. А что мешало просто взять и проверить? Это дело полуминуты - быстрее чем на форуме спросить
1
Форумчанин
Эксперт CЭксперт С++
8165 / 5013 / 1436
Регистрация: 29.11.2010
Сообщений: 13,455
13.10.2017, 11:03 3
Так можно. Но в конструкторе только не вызывайте.

И ; при объявлении класса (ну это уже придирки)
1
Ушел с форума
Эксперт С++
16420 / 7395 / 1185
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
13.10.2017, 11:25 4
+ виртуальный деструктор забыли...
1
Эксперт C
25582 / 15955 / 3414
Регистрация: 24.12.2010
Сообщений: 34,898
13.10.2017, 11:28 5
Однако, мне кажется, что drawShape не должна быть void
1
2851 / 1715 / 352
Регистрация: 09.09.2017
Сообщений: 7,226
13.10.2017, 11:43 6
Отрисовка-то как раз может быть void, вот только масштаб надо по-другому реализовать. На OpenGL1 я бы делал так
C++
1
2
3
4
void drawScalingShape(float scale){
  glScalef(scale, scale, scale); //применяем матрицу трехмерного преобразования
  drawShape();
}
Ну еще, скорее всего, push/pop матриц, но для демонстрации это не нужно.
Но тут вопрос что ТС использует для рисования. Или это чисто абстрактная задача.
0
Эксперт C
25582 / 15955 / 3414
Регистрация: 24.12.2010
Сообщений: 34,898
13.10.2017, 11:49 7
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Но тут вопрос
Не вопрос. Есть представленный ТС код. Там
Цитата Сообщение от VIKT0R Посмотреть сообщение
scale(drawShape());
drawShare() является аргументом. Значится, должон что-то возвращать.
0
9 / 9 / 5
Регистрация: 15.11.2011
Сообщений: 114
13.10.2017, 11:55  [ТС] 8
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
А что мешало просто взять и проверить? Это дело полуминуты - быстрее чем на форуме спросить
Действительно. Правда мне понадобилось больше времени:
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
#include <iostream>
 
 
class Shape
{
public:
    virtual void shape() = 0;
    void offsetShape() { std::cout << "Offset "; shape(); }
};
 
class Circle : public Shape
{
    virtual void shape() { std::cout << "Circle\n"; }
};
 
class Rectangle : public Shape
{
    virtual void shape() { std::cout << "Rectangle\n"; }
};
 
 
int main()
{
    Circle circ;
    Rectangle rect;
 
    circ.offsetShape();
    rect.offsetShape();
 
    return 0;
}
Работает!

Я сомневался потому что думал: "Поскольку функция offsetShape() - базового класса, то и вызовется из нее функция shape() базового же класса". Честно говоря и теперь не до конца осознал все подробности, был бы признателен за объяснение механизма действия.
0
Миниатюры
Виртуальные функции  
Эксперт C
25582 / 15955 / 3414
Регистрация: 24.12.2010
Сообщений: 34,898
13.10.2017, 12:03 9
Цитата Сообщение от VIKT0R Посмотреть сообщение
Правда мне понадобилось больше времени:
Зато пользы больше
0
Ушел с форума
Эксперт С++
16420 / 7395 / 1185
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
13.10.2017, 12:20 10
Цитата Сообщение от VIKT0R Посмотреть сообщение
C++
1
2
3
4
5
6
7
8
9
10
int main()
{
    Circle circ;
    Rectangle rect;
 
    circ.offsetShape();
    rect.offsetShape();
 
    return 0;
}
Здесь нет полиморфизма. Убери везде virtual и получишь тот же результат.
Полиморфизм в C++ работает, когда метод вызывается через ссылку или
указатель на базовый класс.
0
2851 / 1715 / 352
Регистрация: 09.09.2017
Сообщений: 7,226
13.10.2017, 12:40 11
Цитата Сообщение от Байт Посмотреть сообщение
Не вопрос. Есть представленный ТС код. Там
Вы готовы утверждать что это эталон? Может его ТС и написал по своему пониманию. Я же предлагаю альтернативу - как бы сам делал. А в форме scale(drawShape()); я просто не представляю как оно может работать. Если drawShape() это отрисовка, то она вызывается перед scale(). Ладно бы хоть передавался указатель на функцию, но нет - по значению.
Цитата Сообщение от VIKT0R Посмотреть сообщение
был бы признателен за объяснение механизма действия.
Правильнее было бы послать читать литературу, но попробую своими словами. Пример
C++
1
2
3
4
Shape sh; //точно знаем что это Shape - вызываются его методы
Sphere sp; //точно знаем что Sphere - вызываем его методы
Shape *ptr; //точно не знаем, но скорее всего Shape - не-виртуальные методы будут его. Виртуальные - по реальному классу.
ptr = new Sphere; //теперь виртуальные методы будут от класса Sphere, не-виртуальные все еще от Shape
Если на пальцах: не-виртуальные методы вызываются от того класса, которым объявлен экземпляр (если объявлен Shape *ptr - то от Shape). А виртуальные - от того класса, который реально создан (если присвоено ptr = new Sphere - то от Sphere). Если в потомке не переопределен метод (виртуальный ил нет не важно), используется от родителя.

Добавлено через 19 минут
Чуть подробнее:
Кликните здесь для просмотра всего текста
У каждого экземпляра класса есть неявное поле - таблица виртуальных методов. Как следует из названия, там хранятся указатели на виртуальные методы. Не-виртуальные вызываются напрямую, без использования таблицы. Пример (не придирайтесь к оформлению - это не реально работающий код)
C++
1
2
3
4
5
6
7
8
9
class Parent{
  vtable_t *vtable; //указатель на таблицу виртуальных методов, скрытое поле, недоступное для чтения/записи из программы
  void nonvirt(){}
  virtual void virt(){}
};
class Child:Parent{
  void nonvirt(){}
  virtual void virt(){}
};
При этом создаются таблицы
Parent: (void(*virt)(void)) -> Parent::virt()
Child: (void(*virt)(void)) -> Child::virt()

Обратите внимание, что метода nonvirt в таблице нет - он не виртуальный.
Когда мы объявляем экземпляр родительского класса
Parent *obj = new Parent;
то неявному полю vtable присваивается указатель на таблицу Parent. Если же присваиваем экземпляр дочернего класса
Parent *obj = new Child;
то полю vtable присваивается указатель на таблицу Child.
Теперь о том как это работает при вызовах методов
Вызываем не-виртуальный метод obj->nonvirt();. Поскольку obj это указатель на Parent, смотрим есть ли там подходящий метод. Он есть, тогда именно его и вызываем.
Вызываем виртуальный метод obj->virt();. Такого метода в описании Parent нет. Смотрим есть ли он в таблице obj->vtable. Он там есть. Тогда вызываем obj->vtable->virt(). А как мы помним, vtable может указывать либо на таблицу Parent'а, либо Child'а в зависимости от того что мы создали. Таким образом и выбирается к какому классу будет принадлежать метод - к родительскому или дочернему.
.
Если я где-то излишне упростил или усложнил - не стесняйтесь, укажите где и как это описать лучше
1
9 / 9 / 5
Регистрация: 15.11.2011
Сообщений: 114
13.10.2017, 12:53  [ТС] 12
Цитата Сообщение от Убежденный Посмотреть сообщение
Здесь нет полиморфизма. Убери везде virtual и получишь тот же результат.
Полиморфизм в C++ работает, когда метод вызывается через ссылку или
указатель на базовый класс.
Убрал - результат другой :
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
#include <iostream>
 
 
class Shape
{
public:
    void shape() { }
    void offsetShape() { std::cout << "Offset "; shape(); }
};
 
 
class Circle : public Shape
{
    void shape() { std::cout << "Circle\n"; }
};
 
 
class Rectangle : public Shape
{
    void shape() { std::cout << "Rectangle\n"; }
};
 
 
int main()
{
    Circle circ;
    Rectangle rect;
 
    circ.offsetShape();
    rect.offsetShape();
 
    return 0;
}
0
Миниатюры
Виртуальные функции  
Ушел с форума
Эксперт С++
16420 / 7395 / 1185
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
13.10.2017, 12:57 13
Цитата Сообщение от VIKT0R Посмотреть сообщение
Убрал - результат другой
Каюсь, был невнимателен
1
2851 / 1715 / 352
Регистрация: 09.09.2017
Сообщений: 7,226
13.10.2017, 13:39 14
То есть при вызове родительского метода, объект приводится к указателю на родительский тип?
C++
1
2
Circle obj;
obj.offsetShape() --> ((Shape*)&circ)->offsetShape();
0
9 / 9 / 5
Регистрация: 15.11.2011
Сообщений: 114
13.10.2017, 13:50  [ТС] 15
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
То есть при вызове родительского метода, объект приводится к указателю на родительский тип?
Не понял - это кому вопрос?
0
2851 / 1715 / 352
Регистрация: 09.09.2017
Сообщений: 7,226
13.10.2017, 14:02 16
К любому кто знает
Но скорее - мысли вслух
0
9 / 9 / 5
Регистрация: 15.11.2011
Сообщений: 114
13.10.2017, 16:34  [ТС] 17
Разобрался. Если я правильно понял - функция offsetShape() в памяти в одном экземпляре общая для всех объектов всех производных классов, поскольку определена в базовом классе. А из нее вызывается для каждого производного класса свой экземпляр виртуальной функции drawShape(). Графически выглядит так:
0
Миниатюры
Виртуальные функции  
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
13.10.2017, 16:34

Заказываю контрольные, курсовые, дипломные работы и диссертации здесь.

Виртуальные функции
Здрасте! вот задачка, какбэ сделал, но нужны виртуальные функции. даже не знаю как их сделать. ...

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

Виртуальные функции
Пытаюсь понять что это такое но не понимаю. Может есть какие то болие понятные примеры , а то то...

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


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

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

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