Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.56/18: Рейтинг темы: голосов - 18, средняя оценка - 4.56
2 / 2 / 0
Регистрация: 14.07.2011
Сообщений: 49
1

Вопрос по наследованию

09.11.2011, 17:48. Показов 3539. Ответов 52
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Уже неоднократно перечитывал главы про наследования и все равно до конца не разобрался.
Вот например у нас есть класс Четырехугольник, и мы создаем класс Прямоугольник.
И в том и в другом классе есть функции Площадь и если у них будет одинаковая сигнатура, то вызовется функция того класса, объект которого мы создали. Если же сигнатуры совпадать не будут, то нужно явно указать с помощью оператора ::.
Если мы сделаем функцию площади виртуальной, то при одинаковых сигнатурах вызовется функция своего класса. То же в случае разных сигнатур. То есть единственный плюс объявления функций виртуальными, это то что если мы создадим указатель на объект базового класса, он сможет указывать и на объект производного класса?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
09.11.2011, 17:48
Ответы с готовыми решениями:

Вопрос по наследованию
Вопрос насчёт передачи аргументов в конструктор базового класса(создания временного объекта), а...

вопрос по наследованию
начал изучать ООП в РНР. нужно уточнение. имеется класс статей Aticle и дочерний класс...

Вопрос по наследованию
Вывод Hello СHild Hello Base Что сделать чтобы Base не выводило при подобном...

По наследованию
Решил заняться паттернами проектирования и возник вопрос. Допустим есть абстрактный класс public...

52
ниначмуроФ
851 / 535 / 110
Регистрация: 12.10.2009
Сообщений: 1,913
10.11.2011, 08:29 21
Author24 — интернет-сервис помощи студентам
конкретно по вопросу ramarren14

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

То есть единственный плюс
нет не единтственный
0
Заблокирован
10.11.2011, 08:44 22
Цитата Сообщение от PointsEqual Посмотреть сообщение
При объявлении функции виртуальной,
компилятор выбирает функцию, удовлетворяющему тому, что занесено в указатель, а не типу указателя.
забей, и не парься
http://www.devdoc.ru/index.php... l_base.htm
0
Эксперт С++
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
10.11.2011, 08:54 23
Цитата Сообщение от Bers Посмотреть сообщение
можно.
Лучше так не делать...
По поводу наследования и виртуальности.
При наследовании (в С++ - только при открытом) работает принцип подстановки: на место объекта (указателя, ссылки) можно подставить объект производного класса. Обратно - нельзя.
Наболее понятный пример: часы-будильник.
Часы - базовый класс, будильник - наследник. Будильник является часами, но часы - не будильник.

По поводу виртуальности - она без наследования совершенно бесполезна.
А при наследовании полезна, например вот здесь:
C++
1
2
3
4
5
6
7
class Base { ... public:
void f() { cout << "Base"; }
void Call() { f(); }
};
class Derived: public Base
{ ... public: void f() { cout << "Derived"; }
};
Тут мы переопределяем в наследнике вызываемую функцию, а вызывающую наследуем.
Однако!
C++
1
Derived A; A.Call();
вызывается базовая функция f().
А если сделать ее виртуальной, то вызывается своя для каждого типа.
0
Заблокирован
10.11.2011, 09:00 24
это все здорово, только у нас есть конкретная задача - отнаследовать прямоугольник от четырёх угольника. В чем проблема?

Добавлено через 1 минуту
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Derived A; A.Call();
вызовется метод, пренадлежащий Derived
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.11.2011, 09:05 25
Цитата Сообщение от Bers Посмотреть сообщение
вызовется метод, пренадлежащий Derived
Функция Call определена в Base. f не виртуальная, поэтому полиморфного вызова не будет.
0
Эксперт С++
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
10.11.2011, 09:16 26
Цитата Сообщение от Bers Посмотреть сообщение
вызовется метод, пренадлежащий Derived
Нет. Без виртуальности вызовется базовый...
0
Заблокирован
10.11.2011, 10:03 27
Цитата Сообщение от Bers Посмотреть сообщение
void makebigger(Rectange& r); //что за идиотский ассерт запихал в неё Маерс, и что он имел ввиду? Обрати внимание, в качестве аргумента принимается прямоугольник, а не квадрат. Что за ДИБИЛИЗМ? Вешать ассерт, запрещающий прямоугольнику иметь разные примыкающие стороны?
Этот ассерт не запрещает иметь прямоугольнику одинаковые примыкающее стороны! Не надо сочинять небылицы. Выражение внутри этого ассерта всегда равно Тру, внезависимости от того равны смежные стороны, или не равны. В выражении фигурирует только высота прямоугольника на момент начала работы функции и высота по завершению работы функции, ширины вобще там нет, она ни с чем не сравнивается. Равна она высоте или нет - вобще пофигу! Этот ассерт не запрещает иметь прямоугольнику однаковые прилегающие стороны как ты выразился... Смысл этого ассерта в том чтобы показать, что в прямоугольнике одна сторона может изменяться независимо от другой и при открытом наследовании данное свойство передастся и квадрату, что недопустимо!

Нет в примере Мейерса ничего дебильного, он же не виноват, что ты отрываешь книгу и видишь в ней фигу, а потом ещё и копируешь и тоже видишь при этом фигу. У меня уже слов нету, ты уже трижды пытался меня убедить в том, что этот ассерт запрещает иметь одинаковые смежные стороны прямоугольнику, что просто не соответсвует действительности! Или ты думаешь что если повторить ложь много раз, то она станет правдой или я в неё поверю? Нет. Если это какая то шутка, то я её не понял
0
0 / 0 / 0
Регистрация: 10.11.2011
Сообщений: 10
10.11.2011, 10:23 28
У мя аж глаз задергался, так что все таки вызывает A.Call
0
Эксперт С++
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
10.11.2011, 12:23 29
Цитата Сообщение от bob2005 Посмотреть сообщение
У мя аж глаз задергался, так что все таки вызывает A.Call
Без виртуальности f() всегда вызывается функция базового класса.
С виртуальностью f() вызывается функция по типу объекта A.
Аналогично с указателями:
C++
1
2
3
4
Base *p;
Derived A;
p = &A;
p->Call();
При виртуальности вызовется f() наследника, без виртуальности - f() базовая
0
0 / 0 / 0
Регистрация: 10.11.2011
Сообщений: 10
10.11.2011, 14:25 30
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Без виртуальности f() всегда вызывается функция базового класса.
С виртуальностью f() вызывается функция по типу объекта A.
Аналогично с указателями:
Код C++1
2
3
4 Base *p;
Derived A;
p = &A;
p->Call();
При виртуальности вызовется f() наследника, без виртуальности - f() базовая
Объясните нубу зачем делать указатель на base, а затем засовывать в него адрес Derived.
0
Эксперт С++
5055 / 3115 / 271
Регистрация: 11.11.2009
Сообщений: 7,044
10.11.2011, 15:02 31
bob2005, представьте, что у вас есть много разных геометрических фигур (квадраты, прямоугольники, круги и т.д.), все они наследники класса Shape. Пусть пользователь сам выбирает, сколько будет фигур, какие это будут фигуры и в каком порядке они создаются. Каждая фигура умеет себя рисовать (метод draw виртуальный). Фигура создаётся так: в массив указателей на Shape помещается адрес конкретной фигуры, только что созданной. И теперь, чтобы отрисовать все фигуры, нам просто-напросто надо пройтись по всему массиву shapes и для каждого элемента вызвать метод draw.
2
186 / 186 / 21
Регистрация: 08.01.2011
Сообщений: 1,139
10.11.2011, 17:15 32
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Без виртуальности f() всегда вызывается функция базового класса.
С виртуальностью f() вызывается функция по типу объекта A.
Аналогично с указателями:
C++
1
2
3
4
Base *p;
Derived A;
p = &A;
p->Call();
При виртуальности вызовется f() наследника, без виртуальности - f() базовая
С указателями понятно. Без виртуальности будет вызываться метод класса Base, потому что тип указателя - Base.
Но не понятно, почему A.call() вызывает метод f() класса Base, если объект A имеет тип Derived, в котором метод f() переопределен.
Вот пример, где вызывается именно метод класса Derived.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
 
class Base
{
public: void f(){ std::cout << "Base\n"; }
};
class Derived: public Base
{
public: void f(){ std::cout << "Derived\n"; }
};
 
int main()
{
    Derived A;
    A.f();
    system( "pause" );
    return 0;
}
?
0
2 / 2 / 0
Регистрация: 14.07.2011
Сообщений: 49
10.11.2011, 17:47  [ТС] 33
Ну так все верно. А если нужно, чтобы вызвался метод Base, надо A.Base::fun();
0
186 / 186 / 21
Регистрация: 08.01.2011
Сообщений: 1,139
10.11.2011, 18:11 34
Цитата Сообщение от ramarren14 Посмотреть сообщение
Ну так все верно. А если нужно, чтобы вызвался метод Base, надо A.Base::fun();
Ну не где такое не писали, но все равно вызвался метод класса Base.
0
Заблокирован
10.11.2011, 19:23 35
Цитата Сообщение от LosAngeles Посмотреть сообщение
Не надо сочинять небылицы. Выражение внутри этого ассерта всегда равно Тру, внезависимости от того равны смежные стороны, или не равны. В выражении фигурирует только высота прямоугольника на момент начала работы функции и высота по завершению работы функции, ширины вобще там нет, она ни с чем не сравнивается. Равна она высоте или нет - вобще пофигу! Этот ассерт не запрещает иметь прямоугольнику однаковые прилегающие стороны как ты выразился... Смысл этого ассерта в том чтобы показать, что в прямоугольнике одна сторона может изменяться независимо от другой и при открытом наследовании данное свойство передастся и квадрату, что недопустимо!

А теперь смотрим код:

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
void makeBigger(rectange& r)
{
int old = r.height();    //пофиксили высоту 
          //пока ещё валидного прямоугольника
 
r.setWidth( r.width()+10);  //единственный метод, 
                       //который может привести данные 
                                //в "не_валидное состояние"
 
//для прямоугольника метод изменит ширину.
//для квадрата изменит и ширину, и высоту.
 
assert( r.height() == old ); //что здесь проверяется?
 
//По мнению данной функции-манипулятора
//Если у прямоугольника высота изменилась в результате 
//работы методы setWidth() 
//то считать, что была выполнена недопустимая операция
//И поднять тревогу
 
//Какое утверждение заложено в ассерт?
//"при изменении ширины прямоугольника,
//Его высота должна остаться прежней"
 
}
Вопросы:

1. Почему Манипулятор принимает на входе ПРЯМОУГОЛЬНИК, но при этом запрещает быть ему квадратом? Квадрат, это что? Не прямоугольник что ли?

2. Почему Манипулятор таким грубейшим образом пытается вмешиваться во внутреннею работу объекта, и снаружи, игнорируя мнения объектов, за них решает, валидные они или нет?

3. Если вы на вход подадите объект "квадрат". Он у вас отработает. Абсолютно правильно. Так, как от него и ожидается. Но при этом все равно сработает ассерт. Так вот, почему ассерт заламывает абсолютно работоспособный процесс программы?
Ну то есть, никакого сбоя нет в системе, а ассерт все равно всю программу аварийно тормознул.


То есть этот сферический конь в вакууме - хорошая иллюстрация ущербной архитектуры.
Хороший пример того, как делать не надо.
1
290 / 193 / 23
Регистрация: 03.08.2011
Сообщений: 2,824
Записей в блоге: 12
10.11.2011, 19:24 36
Цитата Сообщение от ramarren14 Посмотреть сообщение
Уже неоднократно перечитывал главы про наследования и все равно до конца не разобрался.
Вот например у нас есть класс Четырехугольник, и мы создаем класс Прямоугольник.
И в том и в другом классе есть функции Площадь и если у них будет одинаковая сигнатура, то вызовется функция того класса, объект которого мы создали. Если же сигнатуры совпадать не будут, то нужно явно указать с помощью оператора ::.
Если мы сделаем функцию площади виртуальной, то при одинаковых сигнатурах вызовется функция своего класса. То же в случае разных сигнатур. То есть единственный плюс объявления функций виртуальными, это то что если мы создадим указатель на объект базового класса, он сможет указывать и на объект производного класса?
а ещё ели в базовом классе объявили функцию виртуальной то при наследовании она тоже будет виртуальной так что надобность явления в потомке виртуальной функции нужна лишь для того чтобы программисту было понятно что в базовом классе эта функция виртуальная
0
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.11.2011, 19:35 37
Цитата Сообщение от Bers Посмотреть сообщение
Почему Манипулятор принимает на входе ПРЯМОУГОЛЬНИК, но при этом запрещает быть ему квадратом? Квадрат, это что? Не прямоугольник что ли?
Он не запрещает ему быть квадратом. Он запрещает изменять высоту при изменении ширины. У квадрата нет ширины и высоты, у него есть длина стороны, т.е. использовать метод задания ширины для квадрата не корректно. Вместо проверки рантайм типа объекта проверяется вот такое вот чудо.

Цитата Сообщение от Bers Посмотреть сообщение
Хороший пример того, как делать не надо.
Совершенно верно! Метод должен называться MakeWidthBigger и тогда он становится правильным. С данным названием он недоопределён и его поведение зависит от фактического типа объекта, что не есть хорошо.
0
Заблокирован
10.11.2011, 19:38 38
Цитата Сообщение от ValeryLaptev Посмотреть сообщение
Нет. Без виртуальности вызовется базовый...
Тут нужно понимать одну вещь:

class Derived : public Base {}

У класса Derived нет своего метода Call();
Этот метод есть только у Base

Однако, после компиляции никаких Base уже не существует, как не существует и первоначального Derived . Существует неккий "результирующий" Derived .
Который имеет метод Call() как свой собственный. Как будто бы он был прописан в Derived ,
а не в Base

А вот если у Base и у Derived есть одноименные методы, то у результирующего Derived так же будут оба эти методы "как родные", но жить будут немножко по разным адресам.
Тип данных Derived будит знать "где живёт метод родной для Derived"
Тип данных Base будит знать "где живёт метод родной для Base "

Я ж ссылочку кидал, где расписан "низкоуровневый взгляд на полиморфизм". Там это все очень подробно расжовано.

Добавлено через 1 минуту
Цитата Сообщение от Deviaphan Посмотреть сообщение
Он не запрещает ему быть квадратом. Он запрещает изменять высоту при изменении ширины.
Вот понимаете, вы сами это только сказали. вдумайтесь ещё раз в эти слова.

Это все равно что сказать: "Я не запрещаю тебе быть квадратом, но если ты реально окажется квадратом, то я заломаю нафег всю программу"
1
Делаю внезапно и красиво
Эксперт С++
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.11.2011, 19:47 39
Цитата Сообщение от Bers Посмотреть сообщение
Это все равно что сказать: "Я не запрещаю тебе быть квадратом, но если ты реально окажется квадратом, то я заломаю нафег всю программу"
Да, именно это я и сказал. Квадратом быть не запрещено. Запрещено изменять высоту при изменении ширины.
Ведь согласись, что следующая реализация метода не может быть у квадрата
C++
1
2
3
4
5
6
7
8
class Brick : public rectangle
{
      void setWidth( int w )
     {
          _width = w;
          _height = w*4;
     }
};
Добавлено через 5 минут
Цитата Сообщение от Bers Посмотреть сообщение
где расписан "низкоуровневый взгляд на полиморфизм". Там это все очень подробно расжовано.
К сожалению, там расписано для DecCpp (или что там было), но реализация полиморфизма никак не регламентирована и разработчики компиляторов вольны реализовывать его как им вздумается. Поэтому о "низкоуровневой" реализации лучше даже и не задумываться. Только если с привязкой к компилятору.
0
Заблокирован
10.11.2011, 20:27 40
Цитата Сообщение от Bers Посмотреть сообщение
1. Почему Манипулятор принимает на входе ПРЯМОУГОЛЬНИК, но при этом запрещает быть ему квадратом? Квадрат, это что? Не прямоугольник что ли?
в манипуляторе ничто не запрещает передавать ему прямоугольники и квадраты, он всегда отрабатывает до конца и ассерт никогда не срабатывает. Что ты подразумеваешь в таком случае под "запрещает" не понятно

Цитата Сообщение от Bers Посмотреть сообщение
3. Если вы на вход подадите объект "квадрат". Он у вас отработает. Абсолютно правильно. Так, как от него и ожидается. Но при этом все равно сработает ассерт. Так вот, почему ассерт заламывает абсолютно работоспособный процесс программы?
Переключаем версию на релиз и нет никаких ассертов, что мы получаем? Квадрат стороны которого не равны друг другу, это в какой конторе такой код называется работоспобным?

Цитата Сообщение от Bers Посмотреть сообщение
То есть этот сферический конь в вакууме - хорошая иллюстрация ущербной архитектуры.
Хороший пример того, как делать не надо.
ну мне уже нечего добавить, я прочитал, осознал и полностью разделяю мнение мейерса насчёт данной проблемы, ну а ты можешь продолжать спорить тут и утверждать что мейерс баран, а также автор этой статьи http://www.objectmentor.com/re... es/lsp.pdf и все авторы которые я найду по запросу "square inherit rectangle" тоже бараны и ничего не шарят в ООП. Для меня мнение этих людей как то повесомее будет, чем мнение человека который с четвёртой попытки разобрался в листинге из 5 строк, потому что их точка зрения полностью аргументированна, а у твоей аргументации вобще нет
0
10.11.2011, 20:27
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
10.11.2011, 20:27
Помогаю со студенческими работами здесь

Непонятка по наследованию
Объясните пожалуйста такой момент. Ниже приведу код программы результат ее работы два сообщения:...

Вопросы по наследованию
Вопрос ещё по конструкторам Допустим есть система классов class A { public: ...

Работа в Eclipse по наследованию
Создать абстрактный класс Строение (в качестве свойств использовать координаты, количество окон и...

Задачка по наследованию классов
Вот само задание:https://stepik.org/lesson/24462/step/7?unit=6768 Вот мой код: # Заполнение...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru