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

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

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 17, средняя оценка - 4.82
ramarren14
2 / 2 / 0
Регистрация: 14.07.2011
Сообщений: 49
09.11.2011, 17:48     Вопрос по наследованию #1
Уже неоднократно перечитывал главы про наследования и все равно до конца не разобрался.
Вот например у нас есть класс Четырехугольник, и мы создаем класс Прямоугольник.
И в том и в другом классе есть функции Площадь и если у них будет одинаковая сигнатура, то вызовется функция того класса, объект которого мы создали. Если же сигнатуры совпадать не будут, то нужно явно указать с помощью оператора ::.
Если мы сделаем функцию площади виртуальной, то при одинаковых сигнатурах вызовется функция своего класса. То же в случае разных сигнатур. То есть единственный плюс объявления функций виртуальными, это то что если мы создадим указатель на объект базового класса, он сможет указывать и на объект производного класса?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
PointsEqual
ниначмуроФ
 Аватар для PointsEqual
722 / 516 / 33
Регистрация: 12.10.2009
Сообщений: 1,915
10.11.2011, 08:29     Вопрос по наследованию #21
конкретно по вопросу ramarren14

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

То есть единственный плюс
нет не единтственный
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Bers
Заблокирован
10.11.2011, 08:44     Вопрос по наследованию #22
Цитата Сообщение от PointsEqual Посмотреть сообщение
При объявлении функции виртуальной,
компилятор выбирает функцию, удовлетворяющему тому, что занесено в указатель, а не типу указателя.
забей, и не парься
http://www.devdoc.ru/index.php/conte...rtual_base.htm
ValeryLaptev
Эксперт C++
1004 / 783 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
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().
А если сделать ее виртуальной, то вызывается своя для каждого типа.
Bers
Заблокирован
10.11.2011, 09:00     Вопрос по наследованию #24
это все здорово, только у нас есть конкретная задача - отнаследовать прямоугольник от четырёх угольника. В чем проблема?

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

Нет в примере Мейерса ничего дебильного, он же не виноват, что ты отрываешь книгу и видишь в ней фигу, а потом ещё и копируешь и тоже видишь при этом фигу. У меня уже слов нету, ты уже трижды пытался меня убедить в том, что этот ассерт запрещает иметь одинаковые смежные стороны прямоугольнику, что просто не соответсвует действительности! Или ты думаешь что если повторить ложь много раз, то она станет правдой или я в неё поверю? Нет. Если это какая то шутка, то я её не понял
bob2005
0 / 0 / 0
Регистрация: 10.11.2011
Сообщений: 10
10.11.2011, 10:23     Вопрос по наследованию #28
У мя аж глаз задергался, так что все таки вызывает A.Call
ValeryLaptev
Эксперт C++
1004 / 783 / 46
Регистрация: 30.04.2011
Сообщений: 1,595
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() базовая
bob2005
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.
silent_1991
Эксперт C++
4938 / 3014 / 149
Регистрация: 11.11.2009
Сообщений: 7,024
Завершенные тесты: 1
10.11.2011, 15:02     Вопрос по наследованию #31
bob2005, представьте, что у вас есть много разных геометрических фигур (квадраты, прямоугольники, круги и т.д.), все они наследники класса Shape. Пусть пользователь сам выбирает, сколько будет фигур, какие это будут фигуры и в каком порядке они создаются. Каждая фигура умеет себя рисовать (метод draw виртуальный). Фигура создаётся так: в массив указателей на Shape помещается адрес конкретной фигуры, только что созданной. И теперь, чтобы отрисовать все фигуры, нам просто-напросто надо пройтись по всему массиву shapes и для каждого элемента вызвать метод draw.
Chelioss
179 / 179 / 4
Регистрация: 08.01.2011
Сообщений: 1,131
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;
}
?
ramarren14
2 / 2 / 0
Регистрация: 14.07.2011
Сообщений: 49
10.11.2011, 17:47  [ТС]     Вопрос по наследованию #33
Ну так все верно. А если нужно, чтобы вызвался метод Base, надо A.Base::fun();
Chelioss
179 / 179 / 4
Регистрация: 08.01.2011
Сообщений: 1,131
10.11.2011, 18:11     Вопрос по наследованию #34
Цитата Сообщение от ramarren14 Посмотреть сообщение
Ну так все верно. А если нужно, чтобы вызвался метод Base, надо A.Base::fun();
Ну не где такое не писали, но все равно вызвался метод класса Base.
Bers
Заблокирован
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. Если вы на вход подадите объект "квадрат". Он у вас отработает. Абсолютно правильно. Так, как от него и ожидается. Но при этом все равно сработает ассерт. Так вот, почему ассерт заламывает абсолютно работоспособный процесс программы?
Ну то есть, никакого сбоя нет в системе, а ассерт все равно всю программу аварийно тормознул.


То есть этот сферический конь в вакууме - хорошая иллюстрация ущербной архитектуры.
Хороший пример того, как делать не надо.
Van111
кодер с++
208 / 187 / 4
Регистрация: 03.08.2011
Сообщений: 2,585
Записей в блоге: 12
10.11.2011, 19:24     Вопрос по наследованию #36
Цитата Сообщение от ramarren14 Посмотреть сообщение
Уже неоднократно перечитывал главы про наследования и все равно до конца не разобрался.
Вот например у нас есть класс Четырехугольник, и мы создаем класс Прямоугольник.
И в том и в другом классе есть функции Площадь и если у них будет одинаковая сигнатура, то вызовется функция того класса, объект которого мы создали. Если же сигнатуры совпадать не будут, то нужно явно указать с помощью оператора ::.
Если мы сделаем функцию площади виртуальной, то при одинаковых сигнатурах вызовется функция своего класса. То же в случае разных сигнатур. То есть единственный плюс объявления функций виртуальными, это то что если мы создадим указатель на объект базового класса, он сможет указывать и на объект производного класса?
а ещё ели в базовом классе объявили функцию виртуальной то при наследовании она тоже будет виртуальной так что надобность явления в потомке виртуальной функции нужна лишь для того чтобы программисту было понятно что в базовом классе эта функция виртуальная
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
10.11.2011, 19:35     Вопрос по наследованию #37
Цитата Сообщение от Bers Посмотреть сообщение
Почему Манипулятор принимает на входе ПРЯМОУГОЛЬНИК, но при этом запрещает быть ему квадратом? Квадрат, это что? Не прямоугольник что ли?
Он не запрещает ему быть квадратом. Он запрещает изменять высоту при изменении ширины. У квадрата нет ширины и высоты, у него есть длина стороны, т.е. использовать метод задания ширины для квадрата не корректно. Вместо проверки рантайм типа объекта проверяется вот такое вот чудо.

Цитата Сообщение от Bers Посмотреть сообщение
Хороший пример того, как делать не надо.
Совершенно верно! Метод должен называться MakeWidthBigger и тогда он становится правильным. С данным названием он недоопределён и его поведение зависит от фактического типа объекта, что не есть хорошо.
Bers
Заблокирован
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 Посмотреть сообщение
Он не запрещает ему быть квадратом. Он запрещает изменять высоту при изменении ширины.
Вот понимаете, вы сами это только сказали. вдумайтесь ещё раз в эти слова.

Это все равно что сказать: "Я не запрещаю тебе быть квадратом, но если ты реально окажется квадратом, то я заломаю нафег всю программу"
Deviaphan
Делаю внезапно и красиво
Эксперт C++
 Аватар для Deviaphan
1283 / 1217 / 50
Регистрация: 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 (или что там было), но реализация полиморфизма никак не регламентирована и разработчики компиляторов вольны реализовывать его как им вздумается. Поэтому о "низкоуровневой" реализации лучше даже и не задумываться. Только если с привязкой к компилятору.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
10.11.2011, 20:27     Вопрос по наследованию
Еще ссылки по теме:

C++ Написать простейшую программу по "перегрузке" и "наследованию"
Вопросы по наследованию C++

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

Или воспользуйтесь поиском по форуму:
LosAngeles
Заблокирован
10.11.2011, 20:27     Вопрос по наследованию #40
Цитата Сообщение от Bers Посмотреть сообщение
1. Почему Манипулятор принимает на входе ПРЯМОУГОЛЬНИК, но при этом запрещает быть ему квадратом? Квадрат, это что? Не прямоугольник что ли?
в манипуляторе ничто не запрещает передавать ему прямоугольники и квадраты, он всегда отрабатывает до конца и ассерт никогда не срабатывает. Что ты подразумеваешь в таком случае под "запрещает" не понятно

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

Цитата Сообщение от Bers Посмотреть сообщение
То есть этот сферический конь в вакууме - хорошая иллюстрация ущербной архитектуры.
Хороший пример того, как делать не надо.
ну мне уже нечего добавить, я прочитал, осознал и полностью разделяю мнение мейерса насчёт данной проблемы, ну а ты можешь продолжать спорить тут и утверждать что мейерс баран, а также автор этой статьи http://www.objectmentor.com/resources/articles/lsp.pdf и все авторы которые я найду по запросу "square inherit rectangle" тоже бараны и ничего не шарят в ООП. Для меня мнение этих людей как то повесомее будет, чем мнение человека который с четвёртой попытки разобрался в листинге из 5 строк, потому что их точка зрения полностью аргументированна, а у твоей аргументации вобще нет
Yandex
Объявления
10.11.2011, 20:27     Вопрос по наследованию
Ответ Создать тему
Опции темы

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