Re[3]: Объясняем наследование
От: UA Украина  
Дата: 30.10.14 16:40
Оценка:
T>Возможно имеет смысл -- отнаследовать кнопку с прогрессбаром от просто кнопки... должно быть увлекательно и для студентов наглядно. Но тут меня останавливает вопрос какую библиотеку взять за пример няшного ООП? Я в жизни использовал три GUI-библиотеки, ещё когда что-то кодил под винду. Это VCL, MFC и WTL. Все три с точки зрения правильного плюсового ООП -- ад из костылей, тяжелых наркотиков и легаси-кода.

Это потому что они соорудили ООП поверх native API которое изначально в процедурном стиле было разработано.

T>К моему великому счастью, последние четыре года я к GUI не прикасался. Я слышал, в QT всё несколько получше, но никогда не смотрел внимательней.


В вашем случае Qt наилучший выбор потому как эмулирует GUI в С++ стиле без особых ограничений со стороны операционных систем.
Re[3]: Объясняем наследование
От: UA Украина  
Дата: 30.10.14 16:41
Оценка:
UA>>На примере какой нибудь GUI библиотеки.
AG>Начинающему очень рано в это влезать.
AG>Тем более, что "GUI библиотеки" обычно переполнены тонкостями конкретной технической реализации. Что снижает их ценность в данном случае.

"Нативные" переполнены, "не нативные" не переполнены.
Re[3]: Объясняем наследование
От: AlexGin Беларусь  
Дата: 30.10.14 16:49
Оценка: +1
Здравствуйте, Tilir, Вы писали:

T>Здравствуйте, UA, Вы писали:


UA>>На примере какой нибудь GUI библиотеки.


T>Возможно имеет смысл -- отнаследовать кнопку с прогрессбаром от просто кнопки... должно быть увлекательно и для студентов наглядно. Но тут меня останавливает вопрос какую библиотеку взять за пример няшного ООП? Я в жизни использовал три GUI-библиотеки, ещё когда что-то кодил под винду. Это VCL, MFC и WTL. Все три с точки зрения правильного плюсового ООП -- ад из костылей, тяжелых наркотиков и легаси-кода. К моему великому счастью, последние четыре года я к GUI не прикасался. Я слышал, в QT всё несколько получше, но никогда не смотрел внимательней.


Любая GUI библиотека завязана на технические тонкости (зависящие прежде всего от особенностей ОС и их реализации в этой библиотеке).
Посему, изучение основ ООП на ней — далеко не самый правильный вариант.

Здесь уже достаточно много написали, насчет примеров.
Добавлю только, что на мой взгляд, препод должен дать именно пример на понимание принципов ООП (в данном случае — наследования).
Добиваться, чтобы этот пример делал какую-то сложную функцию, кроме как демонстрация наследования — нет смысла,
так как это только распыляет внимание студента

P.S. Так, например, студент может сделать хороший расчет электрической цепи, правильно применив законы Ома, Кирхгофа и т.д.
При этом, наследовав класс "лампочка", от класса "аккумуляторная батарея", на том основании, что лампочка питается от...
Отредактировано 30.10.2014 18:05 AlexGin . Предыдущая версия . Еще …
Отредактировано 30.10.2014 18:03 AlexGin . Предыдущая версия .
Re[4]: Объясняем наследование
От: AlexGin Беларусь  
Дата: 30.10.14 16:54
Оценка:
Здравствуйте, UA, Вы писали:

UA>>>На примере какой нибудь GUI библиотеки.

AG>>Начинающему очень рано в это влезать.
AG>>Тем более, что "GUI библиотеки" обычно переполнены тонкостями конкретной технической реализации. Что снижает их ценность в данном случае.

UA>"Нативные" переполнены, "не нативные" не переполнены.


Что в твоем, уважаемый UA, понимании "нативные"/"не-нативные"?

Вот взять, например, тот же MFC — нативная библиотека? А тот же VCL?

А что, например, насчет .NET?

P.S. Если, к примеру, взять объект "кнопка", то у него, по логике ООП, должен быть метод "нажатие" (кликнуть — OnClick), но из-за особенностий ОС, этот метод реализуется (по крайней мере в MFC) в виде события.
В результате, для студентов такой пример, ИМХО, не является лучшей демонстрацией ООП
Отредактировано 30.10.2014 17:04 AlexGin . Предыдущая версия . Еще …
Отредактировано 30.10.2014 17:02 AlexGin . Предыдущая версия .
Отредактировано 30.10.2014 16:59 AlexGin . Предыдущая версия .
Re[5]: Объясняем наследование
От: UA Украина  
Дата: 30.10.14 17:21
Оценка:
UA>>"Нативные" переполнены, "не нативные" не переполнены.

AG>Что в твоем, уважаемый UA, понимании "нативные"/"не-нативные"?

"Нативные" используют встроенные возможности операционной системы для построения GUI (например WinAPI) и пользуются как правило встроенной системой сообщений (WndProc).
"Не нативные" эмудируют gui под каждой осью и сами отвечают за обработку gui сообщений.

AG>Вот взять, например, тот же MFC — нативная библиотека? А тот же VCL?

Обе нативные. Так как плотно используют WinAPI для построения окон и обработки сообщений.
QT — не нативная. wxWidgets — нативная.

AG>А что, например, насчет .NET?

WinForms — нативная.

AG>P.S. Если, к примеру, взять объект "кнопка", то у него, по логике ООП, должен быть метод "нажать" (кликнуть), но из-за особенностий ОС, этот метод реализуется (по крайней мере в MFC) в виде события.

AG>В результате, для студентов такой пример, ИМХО, не является лучшей демонстрацией ООП

Это обычно проблема нативных gui, хотя никто не мешает сэмулировать нажатие через метод:
void Button::OnClick(Control* sender, ClickEvent& args)
{
  ::SendMessage(sender->GetHWND(), WM_Click);
}


P.S. Я призываю использовать для обучения "не нативные" gui типа Qt.
Re[6]: Объясняем наследование
От: AlexGin Беларусь  
Дата: 30.10.14 18:01
Оценка: +1
Здравствуйте, UA, Вы писали:

UA>Это обычно проблема нативных gui, хотя никто не мешает сэмулировать нажатие через метод:

UA>
UA>void Button::OnClick(Control* sender, ClickEvent& args)
UA>{
UA>  ::SendMessage(sender->GetHWND(), WM_Click);
UA>}
UA>

Я полагаю, что речь тут не об этом. Не о том, "как изгалиться", чтоб было под ООП
Речь о том, чтобы показать студенту реализацию принципов ООП, сделав это на доступном примере.
ИМХО тут надо что-то достаточно абстрактное и в то же время понятное.
А вот когда человек поймет суть ОО проектирования, тогда уже переходить к конкретике, например в виде тех же GUI библиотек.

UA>P.S. Я призываю использовать для обучения "не нативные" gui типа Qt.

Возможно.
К сожалению, я с Qt не работал, посему мне трудно тут как-то судить.
Re[2]: Объясняем наследование
От: Pavel Dvorkin Россия  
Дата: 31.10.14 06:28
Оценка: +2
Здравствуйте, neFormal, Вы писали:

T>>мне будет стыдно рассказывать, им будет стыдно слушать. Когда я совсем состарюсь пойду воспитателем в детский сад, там мне это очень пригодится, а пока ну нафиг.


F>тебе нельзя преподавать.


Я бы несколько мягче высказался. Нельзя — это уж слишком.

Ситуация же вот какая. Есть (положим) хороший специалист в своей области, и вот он берется преподавать. К сожалению, многие, попавшие в это положение, склонны пренебрегать существующим опытом, и, в уверенности, что коль они хорошие специалисты в своем деле, то уж научить они без труда смогут. При этом не учитывается разница между собственным уровнем и уровнем обучаемых.

Результат — либо по ходу действия придется переориентироваться и действовать в применении к реальности, либо человек уходит из этой области.
With best regards
Pavel Dvorkin
Re[3]: Объясняем наследование
От: Pavel Dvorkin Россия  
Дата: 31.10.14 06:31
Оценка:
Здравствуйте, Tilir, Вы писали:

T>
T>class Mov : public Mnemonic {.....};
T>class Jmp : public Mnemonic {.....};
T>


Если я в какой-то библиотеке увижу классы Mov, Jmp и Add, я стану горячим поклонником класса Meow
With best regards
Pavel Dvorkin
Re[3]: Объясняем наследование
От: Pavel Dvorkin Россия  
Дата: 31.10.14 06:34
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Какие методы можно унаследовать из базового класса Unit?


void Move(int x, int y);
void Move(const Point& pos);
void MoveRel(int dx, int dy);
void MoveRel(const Offset& ofs);
Point& GetPosition();
void SetName(const char* Name); // или string
const char* GetName(); // или string
void Paint() = 0;
Unit* Clone() = 0;
With best regards
Pavel Dvorkin
Отредактировано 31.10.2014 6:41 Pavel Dvorkin . Предыдущая версия .
Re[6]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 07:01
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Если конкретно — показать, как работает виртуальная , скажем, print, почему происходит memory leak при невиртуальном деструкторе, и т.д.


Это всё есть. Но это не о наследовании, а чуть дальше. На механике (очень тонкой, включая даже их реализацию на чистом C) работы виртуальных функций, я останавливаюсь очень подробно. Так же и виртуальные деструкторы.

PD>Ради всего святого, объясни, какое отношение задача трех тел имеет к ООП ? Почему учить ООП надо на примере задачи трех тел — не понимаю. Ее на Фортране можно написать, где ООП и в помине не было. И ничем не хуже будет рассчитана траектория.


Потому что мы учим не ООП а C++. C++ язык мультипарадигменный. И если оказывается, что задача трёх тел проще/естественнее считается без ООП, это очень хорошо. Значит студенты должны это дедуцировать и решить её на C++ без ООП. Поэтому я так критичен к примеру наследования. Мне нужно не какое угодно наследование. Мне нужно наследование только такое, чтобы было очевидно, что оно нужно и очевидно зачем оно нужно.
Re[2]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 07:06
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Если я не прав, откройте свой код и возьмите оттуда столько примеров, сколько надо.


О, это сколько угодно. Сейчас я открою бэкенд LLVM, возьму оттуда сколько надо примеров и студенты повесятся.

Увы, у меня сложновата предметная область. Поэтому я и ищу простые но не дебильные примеры в смежных областях.

BDA>Оффтопик. Главное, что я бы про наследование рассказал — есть места, где оно нужно, поскольку облегчает чтение, а есть места, куда его втыкают либо по глупости, либо из-за ограниченности языка. Пример первого — любая оконная библиотека. Пример второго — boost::operators. Обожаю на диаграмме наблюдать [boost::operators] -> [FileInfo] только потому, что кто-то коллекцию FileInfo решил отсортировать. Не надо так делать. Презренные макросы и то лучше.


Почему оффтопик? На примерах где наследование не нужно и вредно, я останавливаюсь особо.
Re: Объясняем наследование
От: mefrill Россия  
Дата: 31.10.14 07:21
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Но хотелось бы послушать мнение народа. Как бы вы объясняли наследование?


Не понимаю, чем геометрические фигуры не угодили? Слишком просто что-ли, почему стыдно-то? Есть хорошие примеры реальных таксономий, классификация Линнея, например. Можно попробовать реализовать классификацию, которая таксономией не является, ту же таблицу периодических элементов. Не понимаю, зачем студентам головы дурить сложными примерами, если сама тема и так сложна сама по себе.
Re[2]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 08:07
Оценка: +1
Здравствуйте, mefrill, Вы писали:

M>Не понимаю, чем геометрические фигуры не угодили? Слишком просто что-ли, почему стыдно-то? Есть хорошие примеры реальных таксономий, классификация Линнея, например. Можно попробовать реализовать классификацию, которая таксономией не является, ту же таблицу периодических элементов.


Допустим вы построили иерархию геометрических фигур. Или классификацию Линнея. Что дальше вы будете делать с этой иерархией классов? Какие конкретно задачи вам поможет решить наследование, какие без него были бы неоправданно сложны?
Если ответ "никакие", значит это контрпример -- пример ситуации, когда наследование избыточно и поэтому не нужно.
Re[8]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 08:09
Оценка:
Здравствуйте, vpchelko, Вы писали:

V>И какое оно отношение имеет к наследованию?


Не к наследованию, а к вашему примеру наследования на примере List, Set, etc. Необходимость тянуть List от ICollection вызвана именно отсутствием нормальных шаблонов, о чём вам сразу и было сказано.
Re[4]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 08:15
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Если я в какой-то библиотеке увижу классы Mov, Jmp и Add, я стану горячим поклонником класса Meow


Использовать инструкции в интерпретаторе как пример наследования предложил не я.

Можно делать по взрослому но мне то нужен адаптивный пример для студентов.
Re[3]: Объясняем наследование
От: 0BD11A0D  
Дата: 31.10.14 09:58
Оценка:
Здравствуйте, Tilir, Вы писали:

BDA>>Если я не прав, откройте свой код и возьмите оттуда столько примеров, сколько надо.


T>О, это сколько угодно. Сейчас я открою бэкенд LLVM, возьму оттуда сколько надо примеров и студенты повесятся.


T>Увы, у меня сложновата предметная область. Поэтому я и ищу простые но не дебильные примеры в смежных областях.


Ой, да ладно, а. По пути к LLVM вы ни одной задачи не решали? Все эволюционируют от простого к сложному, по дороге прочитав кучу чужого кода и написав кучу своего (не очень удачного).

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

BDA>>Оффтопик. Главное, что я бы про наследование рассказал — есть места, где оно нужно, поскольку облегчает чтение, а есть места, куда его втыкают либо по глупости, либо из-за ограниченности языка. Пример первого — любая оконная библиотека. Пример второго — boost::operators. Обожаю на диаграмме наблюдать [boost::operators] -> [FileInfo] только потому, что кто-то коллекцию FileInfo решил отсортировать. Не надо так делать. Презренные макросы и то лучше.


T>Почему оффтопик? На примерах где наследование не нужно и вредно, я останавливаюсь особо.


Потому, что не сквозной пример наследования из учебника.

Ну хорошо, по теме.

>Рассказать про открытое наследование интерфейса. Показать как писать чисто абстрактные классы. Рассказать про чисто виртуальные функции.


Наследование в C++ и отношение генерализации — разные вещи. То, о чем идет речь, называется «интерфейс». В одном древнем языке понятие интерфейса с его имманентным ограничением на отсутствие какой бы то ни было реализации отсутствует. Реюзаются классы. Не потому, что С. — кретин, а потому, что эволюция. Интерфейсы в массовых языках появились позже. И рассказывать следовало бы из дня сегодняшнего. И примеры приводить из FCL, где они особенно красивы. А потом показывать, как то же самое делается в старом пердуне, заодно отвечая на вопросы, а почему же в базовой библиотеке так не сделано («Патамушта!»).

>Показать как удобно объекты с одним интерфейсом втыкаются в обобщенный код


Всерьез задумался, где же на обсуждаемом языке это видно. COM/CORBA, разве что, да и то это межъязыковые среды.

>Немного блабла про is-a и LSP


Варварина идея по прошествии многих лет вообще кажется мне... Как бы это сказать... Забавным наблюдением, что ли. Но ни в коем случае не руководством к действию. Английское слово 'principle' одновременно означает как правило/закон, так и неизбежное следствие из закона (природы). Если не брать оригинальную формулировку и рассматривать LS Principle во втором смысле, то получится следующее: если правильно выстраивать иерархии, результат обычно будет удовлетворять LSP. Почему — тема для размышлений в свободное время, как говорят преподаватели. Но если телегу поставить впереди лошади и заставлять проверять правильность иерархий по LSP, потом будем иметь очередной говнокод.

Все блабла можно свести к тому, что есть, дескать, такое наблюдение. Полная эмпирика для нашей инженерной науки. Но в смежной гуманитарной области можно подвести теорию с механизмами.

Что касается правильных иерархий, тут уже написали: классификация рулит.

>Упомянуть про наследование реализации, рассказать про просто виртуальные функции, статический и динамический тип, показать примеры когда это полезно и когда вредно. Особо остановиться на виртуальных деструкторах.


Любая оконная библиотека с невиртуальным Draw() и виртуальными DrawNC()/DrawClient()/... показывает все, что надо.

Что касается деструктора, это обработчик события уничтожения, что на нем останавливаться? Что в нем такого особого?

>Упомянуть про закрытое и защищённое наследование. Объяснить что закрытое это такая композиция (и поэтому лучше делать явную композицию), а защищённое вообще никто не знает зачем нужно.


Язык слишком... либеральный. То в нем в интерфейсах реализация допускается, то неоткрытое наследование. Кто-то потом на подобной уродливости строит грязные хаки («потому, что могут», ага), а их дурацкий комитет потом не дает запретить. Так это и надо рассказывать.

>Рассказать про RTTI и dynamic_cast.


Недометаданные в языке без базового класса Object выродились в уродливый костыль. Наследование тут не при чем. Нужда в нем обычно возникает, когда в реальной программе под Уиндоус приходится гонять через WinAPI объекты.

Это не академическое ковыряние в носу, извините. Это жизнь и зарабатывание денег. Чему я бы и учил в первую очередь.
Re[4]: Объясняем наследование
От: Tilir Россия http://tilir.livejournal.com
Дата: 31.10.14 10:26
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA> А ежели кто просит примеров для объяснения другим, я всегда сильно подозреваю недостаточное собственное понимание.


ЧСВ over 9000?

Ни у кого, никогда не может быть достаточного понимания языка C++. Даже его создатель знает его на 6/10. Впрочем, учитывая дальнейшее...
Я, пожалуй, соберу ваши основные мысли в кучку, слитное чтение всего этого бугурта немного поднимает мне настроение:

BDA> В одном древнем языке понятие интерфейса с его имманентным ограничением на отсутствие какой бы то ни было реализации отсутствует.

BDA> Язык слишком... либеральный. То в нем в интерфейсах реализация допускается, то неоткрытое наследование. Кто-то потом на подобной уродливости строит грязные хаки («потому, что могут», ага), а их дурацкий комитет потом не дает запретить. Так это и надо рассказывать.
BDA> Недометаданные в языке без базового класса Object выродились в уродливый костыль. Наследование тут не при чем. Нужда в нем обычно возникает, когда в реальной программе под Уиндоус приходится гонять через WinAPI объекты.
BDA> Это не академическое ковыряние в носу, извините. Это жизнь и зарабатывание денег. Чему я бы и учил в первую очередь.

В общем, с вами всё ясно. Вступать в диспуты с дотнетчиками здесь я считаю таким же оффтопом как и с джавистами. Интернет также полон ими. Ваш тон offensive enough, но и я со своей стороны презираю скриптоязыки для виртуальных машин и в общем согласен на взаимность. Посоветовать что-либо вы мне вряд ли сможете.

Но не удержусь прокомментировать главный перл:

BDA>Что касается деструктора, это обработчик события уничтожения, что на нем останавливаться? Что в нем такого особого?


Понимаете, виртуальный деструктор это то, что надо знать в первую очередь и как отче наш. Зачем он нужен, что будет если его не объявить и отнаследоваться и т.д. Но не в вашем случае, когда вы скриптуете виртуальную машинку и за вами всё чистит сборщик мусора, а в случае, если мы пишем на языке, где память это ресурс и им надо управлять.
Re[7]: Объясняем наследование
От: Pavel Dvorkin Россия  
Дата: 31.10.14 10:50
Оценка: +1
Здравствуйте, Tilir, Вы писали:


T>Это всё есть. Но это не о наследовании, а чуть дальше. На механике (очень тонкой, включая даже их реализацию на чистом C) работы виртуальных функций, я останавливаюсь очень подробно. Так же и виртуальные деструкторы.


Я бы не стал на начальном этапе говорить о том, как это реализуется (на чистом С). Тут все вообще-то на грани С и ассемблера, а они его знают ? Хотя как устроена vtable я им рассказываю.

PD>>Ради всего святого, объясни, какое отношение задача трех тел имеет к ООП ? Почему учить ООП надо на примере задачи трех тел — не понимаю. Ее на Фортране можно написать, где ООП и в помине не было. И ничем не хуже будет рассчитана траектория.


T>Потому что мы учим не ООП а C++. C++ язык мультипарадигменный.


Если речь идет о той части С++, где классы — то это ООП. Остальные элементы мы в этом треде не обсуждали.


>И если оказывается, что задача трёх тел проще/естественнее считается без ООП, это очень хорошо.


Это ни хорошо, ни плохо. Это просто перпендикулярно. ООП — это ООП и его принципы, а задача трех тел — это физика и математика.

>Значит студенты должны это дедуцировать и решить её на C++ без ООП.


Совсем не обязательно. Можно написать библиотеку , скажем, линейной алгебры без классов, а можно и с классами. И выбор тут совсем не очевиден, it depends.

>Поэтому я так критичен к примеру наследования. Мне нужно не какое угодно наследование. Мне нужно наследование только такое, чтобы было очевидно, что оно нужно и очевидно зачем оно нужно.


Я привел в другом месте список методов, которые можно наследовать от базового класса Unit. ИМХО одного этого списка вполне достаточно, чтобы показать, почему наследование от Unit имеет смысл и его стоит использовать, и почему без него будет сложнее и хуже.
With best regards
Pavel Dvorkin
Re[3]: Объясняем наследование
От: mefrill Россия  
Дата: 31.10.14 11:32
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Допустим вы построили иерархию геометрических фигур. Или классификацию Линнея. Что дальше вы будете делать с этой иерархией классов? Какие конкретно задачи вам поможет решить наследование, какие без него были бы неоправданно сложны?

T>Если ответ "никакие", значит это контрпример -- пример ситуации, когда наследование избыточно и поэтому не нужно.

Что значит "без него"? Построение программной модели в виде иерархии классов -- это основной механизм решения задачи в языке C++. Да, конечно, поддерживается и функциональная парадигма и обобщенное программирование. Но, вроде бы в данном случае изучается ООП, так что его и надо проиллюстрировать. Преимущества и недостатки той или иной парадигмы это же отдельная тема. Я вообще сомневаюсь в том, что эту тему студентам надо доносить.
Re[3]: Объясняем наследование
От: Куть  
Дата: 31.10.14 12:26
Оценка: +1
Здравствуйте, Tilir, Вы писали:
T>Допустим вы построили иерархию геометрических фигур. Или классификацию Линнея. Что дальше вы будете делать с этой иерархией классов? Какие конкретно задачи вам поможет решить наследование, какие без него были бы неоправданно сложны?
T>Если ответ "никакие", значит это контрпример -- пример ситуации, когда наследование избыточно и поэтому не нужно.

Эту иерархию классов можно использовать в написании простенького графического редактора.
На примере фигур можно показать, что код с наследованием получается проще и понятнее:
1. Можно нарисовать все фигуры, пробежав по списку указателей std::list<Figure *> и вызывая виртуальный метод draw().
2. Можно найти сумму площадей в пару строчек.
3. Можно сохранить данные всех фигур в файл, заодно можно рассмотреть проблему с чтением из файла и её решение в виде фабричного метода.

А перед этим можно показать пример, где часть этих задач решается без наследования, с помощью длинных switch(type) или указателей на функции.
И добить добавлением нового типа фигуры. С наследованием будет добавлен один класс (изменения локализированы), а без наследования изменения будут размазаны по всей программе.

По-моему, наглядный пример для обучения.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.