У меня недавно закончился небольшой контракт на проведение для молодых программистов чего-то вроде "курса молодого бойца" по C++. Что заставило в очередной раз задуматься на тему "а не стал ли C++ настольно монстрообразным, что обучить новичка практически невозможно?"
Но это сильно глобальный вопрос, я даже не знаю как к нему подойти. Радует лишь то, что сам я C++ изучаю года с 1991-го, когда это был, мягко говоря, совсем другой язык. Гораздо более простой, последовательный и логичный. И мне повезло, что на эту небольшую базу затем инкрементально ложились новые возможности: пространства имен, исключения, шаблоны, STL и т.д., и т.п. Т.е. сам я учил C++ по мере того, как в него добавлялись новые фичи. И поскольку добавлялись они раньше не в таком количестве и не с такой скоростью, то поспевать за ростом сложности C++ где-то до 17-го стандарта еще удавалось. Однако, на C++20 это уже сломалось даже для меня :(
Впрочем, это опять таки уход в сторону от того, о чем хотелось сегодня поговорить.
Попробую сконцентрироваться на более простом вопросе, ответ на который для меня самого совсем неочевиден.
С одной стороны, я противник подхода, при котором разработчика сперва учат языку Си, а затем начинают давать ему C++ по частям. Типа сперва чистый Си, затем немного "Си с классами", затем чуть-чуть шаблонов, затем чуть-чуть STL, затем еще чуть-чуть шаблонов и т.д.
Как по мне, Си и C++ уже очень и очень давно совершенно разные языки. Да, в C++ есть изрядное подмножество чистого Си, но все-таки идеология C++ другая и к таким вещам, как ссылки, классы, деструкторы, нужно привыкать сразу. Чем меньше приемов из чистого Си, тем лучше.
Поэтому вроде как есть смысл новичков сразу учить использовать то хорошее, что есть в C++. Например, std::string для строк вместо char*, std::string_view вместо const char*, std::vector и std::array вместо Си-шных массивов, std::find_if вместо голых циклов и вот это вот все.
Но тут есть серьезная засада, особенно в случаях, когда у новичков за плечами уже есть опыт работы с языками со сборкой мусора (будь то Python или Java, не суть важно). По моим впечатлениям у тех, кто изучает C++ сейчас, есть проблемы с пониманием разницы между values и references. Грубо говоря, люди пишут:
void f(std::string param) {...}
даже не задумываясь о том, что здесь происходит передача по значению, а не по ссылке.
И это, как по мне, проблема, т.к. требуется время и регулярное битье по рукам, чтобы выработать у молодого C++ника навык автоматически отслеживать передачу по значению или передачу по ссылке.
Как следствие, та же move-семантка, без которой современный C++ был бы не нужен в принципе, не очень хорошо заходит. Научить избегать копирования -- это уже задача. А научиться избегать копирования за счет грамотного и регулярного использования move-семантики оказывается задачкой со звездочкой.
Рефлексируя на эту тему я пока придерживаюсь мнения, что виной тому как раз использование готовых "высокоуровневых" возможностей STL. Те же самые std::string и std::vector освобождают программиста от изрядного геморроя, но не дают этому самому программисту понимания того, как же это все работает "под капотом". А без такого понимания сложно программировать на C++ эффективно.
Что заставляет задуматься о том, чтобы учить людей C++у без использования готовых классов и шаблонов из STL.
Грубо говоря, если начинать обучение с того, чтобы молодой C++ник сам написал аналог string-а, vector-а и hash_map-а, сам бы почувствовал где и когда вызываются конструкторы, деструкторы, операторы копирования, где требуется выделить память, где освободить, где скопировать содержимое и столкнуться с проблемой exception safety... Вот если начинать обучение с этого, то может быть у человека будет гораздо больше понимания разницы между передачей по значению или по ссылке, между копированием и перемещением и т.д., и т.п.
Такой подход, на первый взгляд, производит впечатление более перспективного. Мол, мы сперва даем людям базу. А потом на эту базу кладем знания об уже готовых классах/алгоритмах из STL, чтобы можно было ощутить насколько жизнь может быть проще если пользоваться тем, что уже написано.
Но и здесь все не так радужно. Во-первых, такое обучение потребует гораздо больше времени. Ведь если мы научим молодого C++ника делать свой vector, то он все равно еще будет далек от того, чтобы уметь писать нормальный прикладной код.
Во-вторых, по моему субъективному мнению, написание библиотек классов (пусть даже в такой библиотеке будет всего лишь string, vector и hash_map) требует несколько иных навыков, нежели написание прикладного кода. Да, возможно это тараканы в моей голове, но я убежден, что библиотеко-писатели и прикладники -- это разные специалисты. Бывают, конечно, и те, кто одинаково хорош и в том, и в другом, но это такое же частое явление, как и хорошие программисты, вырастающие в хороших менеджеров. В общем, начиная с реализации собственных string и vector можно не получить должного эффекта, если обучаемый больше склонен к решению прикладных проблем, чем к написанию библиотек.
В-третьих, этот подход как раз таки очень близок к подходу, когда мы сперва учим людей чистому Си, а уже затем переходим к использованию высокоуровневых возможностей C++. Близок, не не тождественен. Тем не менее, если молодой разработчик привыкает писать относительно низкоуровневый код с ручными new/delete, alignof/alignas и пр. заморочками, то есть опасность, что он и продолжит в том же духе. Тут людей сразу пытаешься учить пользоваться std::unique_ptr и std::make_unique, а они все равно норовят использовать голые владеющие указатели с new/delete... Что уж говорить о тех, кого специально с самого начала ограждают от std::unique_ptr, std::optional, std::variant, std::vector и т.д.
В общем, я пока для себя ответа не нашел. Не знаю по какому пути следует двигаться, особенно если на серьезное погружение в С++ есть не месяц, а хотя бы полгода.
В завершение еще раз поделюсь своими тягостными впечатлениями о том, что C++ на мой взгляд, стал слишком уж большим и сложным для того, чтобы его мог изучить и освоить человек "со стороны". Речь не о старпёрах, вроде меня, кто за 30 лет в программизме имел возможность осваивать фичи "современного C++" по мере их появления. А о тех, кто про C++ ничего не знал, но должен изучить за относительно небольшое время. Как таким новичкам преподавать тот же С++20? Вот это вопрос.
Хотя, может быть я излишне пессимистичен. Может это лично мне тяжело осваивать C++20, т.к. я уже старый, мозги уже плесневеть начали, много там лишних и устаревших знаний скопилось, да еще и не так уж много возможностей применять C++20 на практике. Отсюда и мое собственное заблуждение о том, что изучать C++20 сложно. А когда человеку 20 лет, все вокруг неизвестно и удивительно, то может быть C++20 залетает в пустую юную голову просто "на ура"... Ведь может же быть и такое.
9 комментариев:
Я начинал с чистого Си. Потом стал использовать С++, просто потому что он использовался в тех программах, с которых я брал пример. При этом я писал прогаммки вообще не зная специфики языка, просто копировал всё из примеров, и как ни странно, именно такое слепое копирование позволило мне писать правильный код несмотря на то, что я не понимал почему надо именно так, а ни как иначе. То есть я реально не знал, например про передачу параметров по ссылке, а просто копировал, потому что так было везде. И только лишь через два года я прочёл первую книгу по плюсам. То есть иными словами, я считаю, что роль учителя заключается в том, чтобы обучать ученика подавая пример на основе правильно написанных программ. Ученик должен начинать с слепого копирования того, что делает его учитель. И, по-хорошему, это не учитель должен бегать за учеником, и пытаться впихнуть тому знания в голову, а у ученика должны возникать вопросы, которые он должен задавать учителю. А уж есть у ученика жажда знаний или нет, толковый он или нет, это вообще к учителю никакого отношения не имеет. Ещё раз повторю, что это задача ученика выудить из учителя все его знания, закидать его вопросами, выжать его как губку. А если ученик этого не делает, то и чёрт с ним. Я понимаю, что вам хочется передать людям какой-то правильный стиль, но я считаю, что учитель может только показать, а уж примут ли знания те кому он показывает, целиком и полностью лежит на плечах самих учеников.
@Stanislav Mischenko
Во многих видах спорта есть первоначальный период обучения новичков базовым движениям/действиям. Типа правильной техники бега в легкой атлетике, правильной техники удара в бильярде, правильной технике выполнения толчка и рывка в тяжелой атлетике и т.д., и т.п. Людям дают "школу" или "базу", на которую затем накладывается туева хуча всяких тонкостей, хитростей и пр.
В 99% случаев необходимость "базы" обуславливается тем, что это уже доведенная до совершенства в течении десятилетий (а то и столетий) техника, которая либо обеспечивает максимальную эффективность, либо сводит к минимуму опасность травм.
Что-то вроде такой "базы" или "школы" было бы хорошо давать и в C++.
Давать эту самую "базу" можно по разному. Пост про то, что в C++ не очевидно, как лучше эту "базу" дать.
Я в таких случаях отправляю человека смотреть Back To Basics с конференции. Но в целом да, быстрого пути в С++ нет. Даже если пытаться ограничиваться классами, в какой-то момент все равно вылезут указатели и крэши.
@eao197
> Пост про то, что в C++ не очевидно, как лучше эту "базу" дать.
Признаю, меня понесло не в ту степь, всё же попробую кратко. Мой пост про то, что учиться лучше всего на примерах. Вся "база" должна содержаться в них. Примеры не должны быть уж совсем игрушечными. Они реально должны делать что-то полезное, должны быть максимально приближены к реальным проектам, только по-меньше. Примеры ученик должен изучать самостоятельно. Чтобы разобраться должна быть справочная литература и учитель, которому можно задать вопрос. Перед началом самостоятельного разбора, учитель должен эту база просто показать, описать в 2-х, 3-х предложениях, разобрать на коротком примере, и это всё. Это и будет отправной точкой для самостоятельного изучения.
Считать ли, например, "голые" указатели "базой" на совести учителя. Если учитель так не считает, не зачем их и показывать. И в примерах их быть не должно. Вопрос на засыпку: если на С++ можно сделать проект без единого "голого" указателя, можно ли считать указатель в С++ базой?
@Stanislav Mischenko
> Считать ли, например, "голые" указатели "базой" на совести учителя.
Ну вот я этим вопросом и задаюсь. ИМХО, современный C++ дает возможность писать код без голых владеющих указателей. И к этому в итоге и следует прийти в процессе обучения. Но если начинать сразу с готовых вещей, то у среднего программиста не будет понимания лежащей ниже механики, что будет вести к неэффективному использованию C++ и потери быстродействия и ресурсов на ровном месте.
Отсюда и проблема: как дать это самое понимание, но при этом не стартовать с того, чтобы решать учебные задачи совсем уж на минималистическом подмножестве C++?
Собственно, ответа на этот вопрос у меня нет.
Такое ощущение, что в комментариях стали решать 2 задачи под одним соусом "обучение С++". Изначальная постановка задачи больше похожа на "надо обучить человека технологии за N академических часов", а когда обучение идет с регулярными код-ревью и хорошими примерами со своей стороны, то это уже скорее речь о долгосрочной работе в команде, где сначала можно сказать, что что-то делать нельзя и только потом чтобы пришло осознание почему так. И наверное нельзя перейти ко второму без первого.
@Stanislav Mischenko:
> Просто показываете как правильно. Коротко, а не подробно, объяснить почему именно так правильно, а по-другому лучше не надо.
Так о том и речь, что это требует затем многократного битья по рукам. Т.е. ты объясняешь почему, люди слушают, а потом пишут код, в котором передачи по ссылке нет. Делаешь ревью, показываешь пальцем. На следующей итерации часть будет исправлена, часть нет, в новом коде где-то будут ссылки, где-то нет. Делаешь очередное ревью... И так снова и снова "до полного удовлетворения".
Отсюда и противоположная точка зрения: если людей заставить писать свои конструкторы/операторы копирования/перемещения, чтобы они сами увидели где и когда это все вызывается, к каким накладным расходам ведет, то тогда они сами начнут задумываться о том, чтобы избегать копирования.
> А вы точно готовите системщиков?
Нет, я системщиков вообще не готовлю.
Но в C++ сейчас есть смысл там, где скорость еще важна, даже если не приходится выжимать последние такты из битов. Типа математики, обработки больших объемов данных, анализ видео/аудио и т.д.
> Может лучше давать задания с фиксированным требованием скорости исполнения программы и размером потребляемой памяти?
Может быть. Но я придерживаюсь правила: сперва сделай корректно, затем заставь это работать быстро.
Если начинать учить людей на высокоуровневых фичах STL (и сторонних библиотек), то некоторые аспекты "сделай корректно" доходят не сразу (например, опасность повисших ссылок, понимание того, что такое use after free) + преждевременная пессимизация, вроде передачи параметров по значению.
@eao197
Мне кажется мы с вами расходимся в понимании зоны ответственности преподавателя. Я считаю, что преподаватель должен только показать как надо, а вот конкретно учится ученик должен сам. Если кто-то игнорирует рекомендации преподавателя, то это никак не вина преподавателя.
На самом деле я бы предложил просто вызвать ученика на откровенный разговор. Пусть он объяснит почему он продолжает делать неправильно, когда ему в сотый раз уже сказали, что так делать не надо. Просто логически порассуждайте с ним и добейтись от него конкретного ответа. По идеи, загнанный в угол аргументами, ученик должен выдать что-то вроде, ну вот в Питоне я так делаю, и до сих пор живой. На что можно ответить, что в С++ приходят не для того, чтобы просто писать программы, а для того чтобы писать эффективные программы, далее, если вы сможете опровергнуть что то, что я вам предлагаю неэффективно, тогда я разрешу ваш код оставить без изменений, а если нет, то давайте без самодеятельности.
Мне кажется, что конструктивный разговор лучше, чем вываливание на итак несправляющихся учеников всей "мощи" С++. В конце концов при правильном подходе изучение С++ должно делится на несколько курсов. Типа С++ для начинающих, далее Углубленное изучение С++. Затем уже С++ для разработчиков игр, С++ для обработки аудио/видео, и.т.д. А ученик должен знать какой именно курс ему нужен.
@Stanislav Mischenko:
> Я считаю, что преподаватель должен только показать как надо, а вот конкретно учится ученик должен сам. Если кто-то игнорирует рекомендации преподавателя, то это никак не вина преподавателя.
Возможно, если речь о преподавателе в ВУЗе, которому нужно отчитать свой предмет, а дальше вообще неизвестно, потребуется ли C++ студентам дальше или нет, такой подход и оправдан.
А когда речь идет о том, чтобы группу неглупых людей за месяц-полтора-два подтянуть до уровня, когда их код можно будет спокойно отдавать на ревью перед вливанием в master, то (на мой взгляд) как-то не так.
Отправить комментарий