У меня тут есть немного студентов на нашей базовой кафедре, которых надо научить плюсам в свободное от работы время. Пока всё шло хорошо, но скоро надо будет объяснить им наследование. Пока план примерно такой:
Рассказать про открытое наследование интерфейса. Показать как писать чисто абстрактные классы. Рассказать про чисто виртуальные функции.
Показать как удобно объекты с одним интерфейсом втыкаются в обобщенный код
Немного блабла про is-a и LSP
Упомянуть про наследование реализации, рассказать про просто виртуальные функции, статический и динамический тип, показать примеры когда это полезно и когда вредно. Особо остановиться на виртуальных деструкторах.
Упомянуть про закрытое и защищённое наследование. Объяснить что закрытое это такая композиция (и поэтому лучше делать явную композицию), а защищённое вообще никто не знает зачем нужно.
Рассказать про RTTI и dynamic_cast.
И дальше отправиться в космос -- множественное наследование, ромбовидные схемы, виртуальные базовые классы, etc.
Но весь этот план требует некоего сквозного примера для которого проводить объяснения. И вот тут у меня затык.
Все книги по C++ которые объясняют наследование вызывают желание убить себе лицо рукой. Вызывали когда я был на втором курсе, вызывают сейчас много лет спустя. Даже дедушка Строструп не уберёгся и унаследовал класс Manger от класса Employer, стыд и унижение. Варианты наследовать котят от собачек, груши от фруктов, кружочки от квадратиков и т.п. не рассматриваются, мне будет стыдно рассказывать, им будет стыдно слушать. Когда я совсем состарюсь пойду воспитателем в детский сад, там мне это очень пригодится, а пока ну нафиг.
Нахожусь в активном поиске нормального вменяемого примера наследования. Пока что придумал такую идею -- наследовать от "графа вообще" его частные случаи -- CFG, DAG, дерево. Близко к моей основной теме (оптимизирующие компиляторы), можно что-то рассказать дополнительно.
Но хотелось бы послушать мнение народа. Как бы вы объясняли наследование?
Здравствуйте, Tilir, Вы писали:
T>Все книги по C++ которые объясняют наследование вызывают желание убить себе лицо рукой. Вызывали когда я был на втором курсе, вызывают сейчас много лет спустя. Даже дедушка Строструп не уберёгся и унаследовал класс Manger от класса Employer, стыд и унижение. Варианты наследовать котят от собачек, груши от фруктов, кружочки от квадратиков и т.п. не рассматриваются, мне будет стыдно рассказывать, им будет стыдно слушать. Когда я совсем состарюсь пойду воспитателем в детский сад, там мне это очень пригодится, а пока ну нафиг.
Не по теме, но, честно говоря, лучше бы студентам рассказывали более подробно про функциональную декомпозицию. Имхо, это сейчас новички не умеют делать от слова совсем. Сразу начинают лепить куда придётся эти наследования, инкапсуляции и прочие полиморфизмы, в то время как всё это стоит применять уже после проведения декомпозиции. Похоже и у дедушки Страуструпа та же проблема...
Здравствуйте, Tilir, Вы писали:
T>Но хотелось бы послушать мнение народа. Как бы вы объясняли наследование?
То, что пример найти не удается, это как бы намекает на то, что Вы ищете среди примеров на наследование реализации.
Если говорить о наследовании интерфейса, пример будет легче найти, например какой-нибудь сервис, одна реализация лезет в БД, другая работает в памяти,
а интерфейс у них один и тот же. Это сплошь и рядом используется для тестирования.
Вообще я бы рассказал немного, как это сделано в других языках. Хотя бы в том же C#, там ключевое слово interface есть.
Боюсь что многочисленные тонкости C++ отвлекут внимание студентов от сути концепций...
Вообще я бы больше акцентировал внимание на виртуальных (и чисто виртуальных) методах, студенты не понимают что это и зачем нужно.
T>Hi,
T>У меня тут есть немного студентов на нашей базовой кафедре, которых надо научить плюсам в свободное от работы время. Пока всё шло хорошо, но скоро надо будет объяснить им наследование. Пока план примерно такой:
T>
T>Рассказать про открытое наследование интерфейса. Показать как писать чисто абстрактные классы. Рассказать про чисто виртуальные функции. T>Показать как удобно объекты с одним интерфейсом втыкаются в обобщенный код T>Немного блабла про is-a и LSP T>Упомянуть про наследование реализации, рассказать про просто виртуальные функции, статический и динамический тип, показать примеры когда это полезно и когда вредно. Особо остановиться на виртуальных деструкторах. T>Упомянуть про закрытое и защищённое наследование. Объяснить что закрытое это такая композиция (и поэтому лучше делать явную композицию), а защищённое вообще никто не знает зачем нужно. T>Рассказать про RTTI и dynamic_cast. T>И дальше отправиться в космос -- множественное наследование, ромбовидные схемы, виртуальные базовые классы, etc. T>
T>Но весь этот план требует некоего сквозного примера для которого проводить объяснения. И вот тут у меня затык.
T>Все книги по C++ которые объясняют наследование вызывают желание убить себе лицо рукой. Вызывали когда я был на втором курсе, вызывают сейчас много лет спустя. Даже дедушка Строструп не уберёгся и унаследовал класс Manger от класса Employer, стыд и унижение. Варианты наследовать котят от собачек, груши от фруктов, кружочки от квадратиков и т.п. не рассматриваются, мне будет стыдно рассказывать, им будет стыдно слушать. Когда я совсем состарюсь пойду воспитателем в детский сад, там мне это очень пригодится, а пока ну нафиг.
T>Нахожусь в активном поиске нормального вменяемого примера наследования. Пока что придумал такую идею -- наследовать от "графа вообще" его частные случаи -- CFG, DAG, дерево. Близко к моей основной теме (оптимизирующие компиляторы), можно что-то рассказать дополнительно.
T>Но хотелось бы послушать мнение народа. Как бы вы объясняли наследование?
T>--- T>With best regards, Konstantin
В Java очень хорошо наследование показано стандартными коллекциями: List, Map, Set ... etc
А вот например С++ виртуальное наследование, я до сих пор не видел прикладных задач — где это нужно.
Здравствуйте, Miroff, Вы писали:
M>Что-нибудь рассчетное, например, орбиты планет. Важно не просто наследование, в преимущества которые оно дает, в частности полиморфизм.
А вы не могли бы подробней про орбиты планет? Что наследуется, какие преимущества даёт?
Здравствуйте, Tilir, Вы писали:
T> немного студентов
T> Варианты наследовать котят от собачек, груши от фруктов, кружочки от квадратиков и т.п. не рассматриваются, мне будет стыдно рассказывать, им будет стыдно слушать.
Про кошечек и собачек может быть и стыдно, но студенты наверняка играют в игры. Вот на примере иерархии игровых объектов стратегии и можно объяснить наследование. Например, базовый класс Unit. От него наследуются всякие там танки, машины, пехота. У каждого из этих юнитов своё поведение. Подошла армия к реке — плавающие юниты двинулись вплавь, неплавающие в обход, искать брод или мост. Подошли к болоту — гусеничные ломятся напролом, колёсные в объезд. Это поведение реализовано в перегруженном виртуальном методе Move. Студентам должно быть интересно.
Здравствуйте, Tilir, Вы писали:
T>Здравствуйте, Miroff, Вы писали:
M>>Что-нибудь рассчетное, например, орбиты планет. Важно не просто наследование, в преимущества которые оно дает, в частности полиморфизм.
T>А вы не могли бы подробней про орбиты планет? Что наследуется, какие преимущества даёт?
Здравствуйте, Tilir, Вы писали: T>Но хотелось бы послушать мнение народа. Как бы вы объясняли наследование?
В качестве намека.
При реализации интерпретатора виртуальной машины систему команд удобно представлять иерархией наследования с абстрактным классом во главе.
И здесь же — паттерн Команда можно объяснять. И функтор — тоже.
В нашей среде Semantic операторы учебного языка построены в иерархию наследования на основе паттерна Наблюдатель.
Прога представляет собой многоуровневый список списков — семантическое дерево. Один оператор — это узел дерева.
Обход дерева — это Визитор.
Изменения в каком-нить узле — Наблюдатель работает.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Miroff, Вы писали:
M>Коллекции лучше не трогать, а то придется про ко- и контрвариантность рассказывать.
А нафига так преподавать?
Для меня на практике, наследование — это формальность — детали реализации.
Тот же List у нас в массиве хранится (ArrayList) либо на удаленном сервере (своя реализация AbstactCollection — которая по сети данные гоняет), либо в базе данных (spring / jpa).
Ну да для конечного пользователя — это просто List — интерфейс.
Здравствуйте, vpchelko, Вы писали:
V>Здравствуйте, Miroff, Вы писали:
M>>Коллекции лучше не трогать, а то придется про ко- и контрвариантность рассказывать.
V>А нафига так преподавать?
V>Для меня на практике, наследование — это формальность — детали реализации.
V>Тот же List у нас в массиве хранится (ArrayList) либо на удаленном сервере (своя реализация AbstactCollection — которая по сети данные гоняет), либо в базе данных (spring / jpa).
V>Ну да для конечного пользователя — это просто List — интерфейс.
А там можно перейти к прелестям Java — Proxy Injection. Которые в рантайме строят нужный нам (конфигу) объект.
Здравствуйте, koodeer, Вы писали:
K>Про кошечек и собачек может быть и стыдно, но студенты наверняка играют в игры. Вот на примере иерархии игровых объектов стратегии и можно объяснить наследование. Например, базовый класс Unit. От него наследуются всякие там танки, машины, пехота. У каждого из этих юнитов своё поведение. Подошла армия к реке — плавающие юниты двинулись вплавь, неплавающие в обход, искать брод или мост. Подошли к болоту — гусеничные ломятся напролом, колёсные в объезд. Это поведение реализовано в перегруженном виртуальном методе Move. Студентам должно быть интересно.
Присоединюсь к предложению рассказывать на примере RTS-игры.
Я именно так и объяснял когда-то стажёрам, нафига нужны виртуальные функции. Когда выделяем мышкой группу объектов — мы получаем коллекцию с указателями на объекты разных классов. Все они реализуют один и тот же интерфейс, но реализуют его каждый по-своему.
Здравствуйте, LaptevVV, Вы писали:
LVV>При реализации интерпретатора виртуальной машины систему команд удобно представлять иерархией наследования с абстрактным классом во главе.
Слишком тонкий намёк, хотелось бы чуть подробней.
class Mov : public Mnemonic {.....};
class Jmp : public Mnemonic {.....};
Но какие могут быть методы у "Мнемоники вообще"? Там даже execute будет с разным числом параметров.
До паттернов пока рано. Когда начинаются шаблоны я возможно упомяну паттерны и их реализацию метапрограммами Александреску-стайл, но мне кажется это слишком разрушительно. Пока что речь всего лишь о наследовании.
Здравствуйте, Abyx, Вы писали:
A>y STLа классы называются meow и purr, при этом он офигенно крут, и объясняет вещи нормально.
Лававеевские подкасты это плюсовое безумие, рассчитанное на расширение кругозора профессионалов.
template<class U>
struct purr < U, enable_if<std::is_integral<U>::value > > {};
Хм. Ну да. Purr.
Близкий аналог -- известная книга Дона Бокса про COM Internals, где кокласс Горилла маршалит экземпляр банана. Меня всегда несколько раздражали эти ужимки и прыжки. Да, профи через кисок продерётся и мессадж поймёт. Но 9/10 дропнут на середине. Ибо от кисок -- тошнит. Программист всё-таки занят делом, по большей части -- серьёзным и полезным.
Здравствуйте, vpchelko, Вы писали:
V>В Java очень хорошо наследование показано стандартными коллекциями: List, Map, Set ... etc
Очевидно это сделано из-за убогости джавовских шаблонов, не позволяющих иметь ортогональные алгоритмы и контейнеры. На ранних плюсах писалось много таких контейнерных библиотек для бедных, все умерли. Там дикий оверхед на виртуальные вызовы и марсианские требования к тому что и как туда можно класть.
Как раз на примере vector, map, set, etc я объясняю случаи когда нужно обходиться без наследования.
Здравствуйте, Tilir, Вы писали:
T>Как раз на примере vector, map, set, etc я объясняю случаи когда нужно обходиться без наследования.
Обоснуй
T> ортогональные алгоритмы и контейнеры
По подробнее, я не знаю таких терминов.
T> Там дикий оверхед на виртуальные вызовы и марсианские требования к тому что и как туда можно класть.
Это вы о чем вообще?
П.с. в Java например: есть TreeMap (эквивалент std::map), HashMap, ConcurentTreeMap ConcurentHashMap. У каждой реализации есть свои преимущества. и они реализуют одну суть — ассоциативный массив.