Привет, Хабр! Представляю вашему вниманию перевод статьи "Instructor’s Guide for Programming" автора Бьярне Страуструпа (дата последнего посещения: 20.12.2017).
От переводчика
Бьярн Страуструп известен как разработчик языка программирования C++. Книги его авторства лежат на столах многих маститых разработчиков.
Особенно стоит отметить учебник по основам программирования "Программирование: Принципы и практика с использованием C++". Бьярн написал его для студентов Техасского университета агрокультуры и машиностроения, в котором он долгое время преподавал курс для студентов. В дополнение к самому учебнику, на сайте www.stroustrup.com можно найти Руководство инструктора. Изложенный в нем опыт преподавания курса основ программирования может быть полезен не только для курса по C++. В руководстве можно найти ответы на типичные вопросы преподавателей: Как подавать материал? Как бороться с расслоением группы по навыкам? Насколько глубоко нужно погружаться в тему?
Мне не удалось найти русской версии Руководства инструктора к книге "Программирование: Принципы и практика с использованием C++", поэтому я решил перевести его и опубликовать.
Резюме
Этот текст — просто сборник наблюдений и выводов. Они могут быть полезны, если вы ведете курс по книге "Программирование: Принципы и практика с использованием C++".
Предположения
Я предполагаю, что вы профессор, лектор, инструктор, ассистент преподавателя. Может быть ваша должность называется иначе. Главное — вы занимаетесь или планируете заниматься преподаванием курса по книге "Программирование: Принципы и практика с использованием C++". Я предполагаю, что вы прочитали (хотя бы) предисловие книги и Главу 0 "Обращение к читателям". Если это не так, прошу вас уделить этому некоторое время перед чтением этого материала.
Я предполагаю, что ваши студенты до сих пор не программировали, имеют отдаленное представление о программировании или использовали язык, который отличается от C++. В основном я решаю проблемы первых двух групп.
После общей информации о главе, вы можете найти комментарии к отдельным разделам. Используйте эти заметки при планировании лекции, основанной на данной главе. Слайды моих презентаций доступны на сайте. Если вы будете использовать мой материал, то, я надеюсь, вы предложите как его можно улучшить.
Мы будем рады обратной связи и описанию вашего преподавательского опыта.
Замечание: Этот текст всего лишь заметки и ничего более. Никто не занимался техническим редактированием этого текста. Не ожидайте завершеннности материала. Напротив, он будет дорабатываться на основании дальнейшего опыта и по мере его выражения в этих записях. Я планирую обновлять эти заметки (они доступны на сайте поддержки).
Прим. перев: Последнее обновление было 10/7/2011.
Введение
В народе ходят стойкие убеждения, которые мешают научиться писать хорошие программы:
- Это один из самых сложных технических навыков.
- Это навык, требующий редкого таланта.
- Программы пишутся подростками-социопатами в полной изоляции, в основном ночью.
- Программирование это создание видеоигр про насилие и жестокость.
- Этот навык требует продвинутого знания математики.
- Это занятие, которое совершенно отличается от повседневного мышления.
- Это не помогает людям.
Главная ваша задача, как преподавателя программирования, в том, чтобы минимизировать влияние этих мифов.
У нас есть одна успешная практика. Каждые три или четыре лекции мы уделяем несколько минут на описание интересных прикладных задач, решаемых с помощью программирования. В Главе 1 вы можете получить представление, что мы считаем интересным. Студенты лучше воспринимают сухую теорию, если им указать на область ее применения. Важно, чтобы эти комментарии были краткими и не превратились в нравоучения. Если вы сами не чувствуете, что какая-то задача увлекательна, не пытайтесь убедить в этом студентов. Они раскусят ваш ложный энтузиазм в первый же миг. Во время рассказа мы опираемся на наш личный опыт, на то что мы видели в командировке, слышали в новостях или читали в журнале. Обязательно приведите примеры: пару фотографий или даже реальные устройства, например iPod, мобильник, часы.
Обратите внимание, что у студентов могут быть различные интересы. Некоторых студентов могут заинтересовать новейшие разработки в видеоиграх. Однако такие факты могут создать негативное представление о программировании у более серьезных студентов, которые еще не решили, нужно ли им вообще изучать программирование, компьютерные науки и информационные технологии. Вторую группу, в разных пропорциях, могут заинтересовать примеры из биологии и биомедицины. (Прим. перев: Учебник был разработан в Техасском университете агрокультуры и машиностроения, скорее всего это местная специфика) Очень важно рассказать обо всем разнообразии задач, иначе студенты отключатся и сохранят обывательский, лишенный воображения взгляд на программирование. При любой возможности, старайтесь связать код с примером из реального мира: "Гугл использует техники как в STL" — хороший пример для частей по STL, графический интерфейс iPod подходит для главы о GUI, а пример с самолетом — к главам про работу с железом и корректность программ.
Необходимо донести идею о том, что написание и запуск маленьких программ это часть "чтения главы". Мы обнаружили, что для многих новичков материал в первых главах выглядит слишком простым, чтобы воспринимать его серьезно, а некоторые фрагменты — слишком абстрактными, чтобы тратить на них время. Студенты часто приходят с небольшим опытом в программировании и поэтому считают, что они знают все. По крайней мере все, что им вообще могут рассказать в первый месяц. Из за этого они могут даже пропускать первые лекции. Другие до сих пор не сталкивались с программированием, но считают, что могут изучить его с помощью книг. В школе у них сложилась иллюзия, что они сами могут отличить важное от несущественного. Некоторые из сильных студентов недооценивают время и усилия, которые им нужно потратить. Школьный материал давался им без особых усилий. В реальности это не так. Чем раньше они это поймут, тем лучше.
Чтобы преуспеть в изучении курса, студенту в первую очередь нужно "тратить время" на выполнение заданий. Это важнее, чем предыдущий опыт программирования, оценки в школе или интеллектуальные способности (насколько мы можем судить). Для минимального знакомства с материалом достаточно выполнить задания. Однако для полного освоения курса необходимо присутствовать на лекциях и выполнять упражнения в конце глав. (Прим. перев. В конце каждой главы книги есть и задания, и упражнения) Мы выделяем время в "лаборатории" и ассистентов преподавателя для помощи с упражнениями. Одна из полезных практик — разделение студентов на маленькие группы. При этом желательно составлять группы из людей с разнообразным опытом программирования. В каждой главе приложите все усилия, чтобы каждый студент:
- Выполнил "Задания". Тут будет полезен инструктор, ассистент или просто опытный студент, который может помочь с возникающими "небольшими практическими проблемами".
- Прочитал "Послесловие" и ответил хотя бы на некоторые вопросы. Это может потребовать повторения некоторых фрагментов текста. Прилежные студенты сделают это самостоятельно. А тех, кому это на самом деле нужно, придется "попинывать".
- Выполнил несколько упражнений. Они грубо отсортированы по сложности (простые в начале) и последующие зачастую основаны на предыдущих.
Берегитесь гиков-всезнаек, которые будут убеждать других студентов в своей правоте, аргументируя школьными знаниями или информацией с форумов в интернете. Использование массивов и указателей вместо векторов и строк это "светлая идея", которая наносит наибольший ущерб. Ее жертвами становятся невинные новички, которым приходится продираться через проблемы переполнения буфера, выделения памяти фиксированными блоками, использования strcpy() и т.п. задолго до того, как они будут к этому готовы.
Считается, что мы пишем для "элитных студентов топовых вузов". Это очень лестный отзыв для первокурсников TAMU, но реальность как всегда не такая. У нас не самый строгий отбор, да и студенты не настолько мотивированы осваивать курс, как нам бы хотелось. Если быть честными, то первые несколько сотен студентов были инженерами-электриками и компьютерными инженерами, которые не видели своего будущего в разработке софта (сам курс просто был обязательный). Мы начали учить первокурсников специальности компьютерных наук намного позже, когда наши результаты вышли на уровень этих специалистов.
Если вы преподаете в большой группе, далеко не все будут справляться. В этом случае вам нужно принять тяжелое решение: замедлиться для помощи слабым студентам или держать темп и потерять их. Всеми фибрами души вы бы будете стремиться притормозить и помочь. Помогайте. Всеми доступными способами. Но ни в коем случае не замедляйтесь. Это будет нечестно для умных, подготовленных и упорно работающих студентов — от отсутствия сложных задач они заскучают, и вы их потеряете. Раз вы в любом случае кого-нибудь потеряете, пускай это будут не ваши будущие звезды, а те, кто никогда не станет хорошим разработчиком или ученым. Компьютерные науки и разработка программ и так уступают студентов более интересным областям науки. Пожалуйста, не усугубляйте эту проблему.
Мы убеждены, что студенты оправдают наши ожидания и наш курс, кажется подтверждает это. Опросы показывают, что слушатели курса занимаются на 25% больше часов и отзываются о нем на 25% лучше, чем о типичной дисциплине для первокурсников. Так совпало, что это был наибольшая оценка удовлетворенности в инженерном колледже). Этот курс не сложнее, чем средняя биология, физика или математика для первого курса для типичных выпускников американских школ. Обычно в устоявшихся академических областях знаний, вроде биологии, подготовка в школе лучше, чем к компьютерным наукам.
Некоторые предполагают, что курс был успешен "потому что его вел Бьярн Страуструп". Это тоже очень лестно, но тоже не правда. Первокурсникам не свойственно благоговение перед чьей то репутацией, к тому же многие поначалу (к моему сожалению) намного более заинтересованы в оценках, чем в изучении серьезных технических деталей. Так или иначе, последние 4 семестра курс вели Пит Питерсен, Уолтер Доэрти и Ронни Вард; они очень опытные преподаватели старших курсов; Я занимаюсь только случайными "гостевыми лекциями".
Первоначально курс вели с двумя лекциями в неделю плюс занятия в в лабораториях под руководством ассистентов преподавателей. Этого не хватало и нам пришлось срезать углы. Позже, курс был расширен до трех лекций в неделю плюс лабораторные и стало лучше. Только тогда мы смогли посвятить время разбору примеров и добавить больше примеров из книги. Скорее всего, сильным студентам это было не нужно (эта информация есть в книге), но это помогло большинству студентов. В книге все еще есть много информации, которую хотелось бы представить на лекциях. Мы отводим каждому слайду по 2 минуты. Если у вас такая же скорость, вы обнаружите, что некоторые слайды придется пропустить, чтобы уложиться в лекцию на 50 или 60 минут. Мы тоже так делаем.
Сейчас мы решили поэкспериментировать и растянули курс на два семестра с двумя лекциями в неделю. Я не ожидаю успеха, потому что проходит довольно много времени между началом курса и началом разбора "интересных" задач. Будем надеяться, что я ошибаюсь.
Далее вы найдете обзоры к каждой главе. Они могут быть полезны преподавателям, но я решил не помещать их в книгу. Контрольные вопросы оказались полезнее для студентов, чем обзоры, в которых находятся все ответы (и которые нравятся по неправильной причине).
Мы всегда успеваем пройти от главы 1 до главы 22 за 15-недельный семестр. Это оставляет нам несколько дней на сессию и время для необходимого группового проекта в последние (примерно) три недели (одновременно с заключительными лекциями). Главы 22-27 на самом деле не обязательно должны идти в этом порядке. Вы можете читать их при необходимости. Они могут быть весьма полезны для поддержки более амбициозных проектов.
Обратите внимание, это значит что мы используем 1264-страничную книгу для курса, который описан на 812 страницах. (прим. перев. в переводе 1329 страниц всего и 827 на основные главы). Все остальное это дополнительный метарил: специальные темы для заинтересованных студентов, справочные материалы и т.п.
Практическое замечание: Слайды немного перегружены информацией потому что некоторые студенты (слишком) опираются на информацию в них и могут упустить материал в книге, которого нет на слайдах. Кроме того, это помогает инструктору, у которого было недостаточно времени на подготовку (весьма распространенный феномен). Код написан шрифтом 20пт. Этого достаточно для большинства в аудитории на 200 человек. В редких исключительных случаях, я использую шрифт 18пт. Мне не очень нравятся украшательства на слайдах, поэтому там нет мудреных переходов или занимательной анимации. Если темный фон вас смущает, поменяйте дизайн на черно-белый. Наш коллектив в TAMU так и не пришел к единому мнению на этот счет. Если у вас есть больше фотографий для иллюстраций или они лучшего качества, прошу вас прислать их мне. Студентам нравятся красивые фотографии если они связаны с темой занятия.
Перед каждой главой ознакомьтесь с информациях об ошибках (www.stroustrup.com/Programming/errata.html). Опечатки, особенно во фрагментах кода, могут очень помешать в обучении.
Вы можете добавлять слайды для решения тактических вопросов. Мы делаем так же. Я удалил все слайды, относящиеся к TAMU, чтобы вам не пришлось этого делать. Не стесняйтесь добавлять свое имя на первом слайде, но если вы не делали значительных изменений, пожалуйста, оставьте мое имя и ссылку на сайт поддержки. Я публикую PowerPoint для того, чтобы упростить изменение.
Благодарности
Благодарю Лоуренса "Пита" Питерсена, Уолтера Доэрти и Ронни Варда за конструктивные комментарии слайдов и этого руководства.
Глава 0: Обращение к читателям
Очевидно, что "Глава 0" пронумерована странно. "0 is odd" одновременно значит "0 — нечетный" и "0 — странный". Это намеренная нелепость. Ее прочтут только некоторые студенты, а большинство пропустят. Немногие из тех, кто никогда не программировал раньше смогут понять ее. Ее основная цель — дать вам, профессору, лектору, ассистенту преподавателя и т.п, общее представление о моих целях и методах.
Так почему же "Глава 0"? Поскольку нумерация в C++ — как шкала на рулетке — начинается с 0, а не с 1. Чтобы дать людям зацепку: "Тут происходит что-то необычное". Чтобы обозначить, что эта глава отличается от серии других глав с лекциями и домашним заданием. В некоторой степени она передает "мета-информацию".
Мы не рекомендуем студентам читать Главу 0, однако многие будут. Мы не предлагаем проводить лекций по материалу Главы 0. В то же время она содержит идеи, которые нужно по мере необходимости использовать во всех лекциях и обсуждениях. Она отвечает на вопросы "Почему мы делаем так?" и "Действительно ли нам нужно делать так?" задолго до того, как студенты зададут их во время курса. Иногда вам будет достаточно ответить "Теперь ты готов прочитать Главу 0".
Тем не менее я прошу вас прочитать Главу 0 и отнестись к ней со всей серьезностью. На начальном этапе постоянные попытки улучшить курс путем добавления материала могут привести к разрушительным последствиям. До Главы 12 студенты находятся на грани перегрузки. Они двигаются вперед только если их постоянно мотивировать. После каждой главы они способны сделать немного больше, чем раньше, даже если они этого не понимают. Пожалуйста, не пытайтесь расширить курс, принуждая студентов на этом этапе понять абсолютно все. Ваша задача в том, чтобы подготовить у них такую базу навыков, к которой они самостоятельно смогут добавлять новые знания и при этом осмысленно применять их.
Если вы ассистент преподавателя, то прежде чем пытаться что-либо объяснять студентам, пожалуйста, прочитайте книгу целиком. Материал отличается от C++ ваших отцов. Кроме того, откройте лекции или, хотя бы, прочитайте текст слайдов. Трудно помогать студентам, если вы не знаете какими фактами они располагают. Информация на слайдах к главе это наилучшая шпаргалка по самой главе.
По моему мнению, одна из убийственных и разрушительных практик это детальное изучение базовых типов C++, их взаимодействия, а также подробной работы ветвления, циклов и рекурсии. На этом этапе это просто мертвый груз. Вы многие годы можете быть отличным разработчиком, но при этом не сможете объяснить (абсурдные) правила преобразования из unsigned short
в int
.
Глава 1 и 2
Чтобы приступить к программированию, студенту нужно:
- Представление о важных и полезных задачах для разработчиков ПО, стремление добиться успеха при их решении и немного идеализма.
- Наглядный пример, который они смогут написать, запустить и модифицировать ради эксперимента.
И то и другое важно. Мы предлагаем посвятить этому половину лекции 1.
Цель главы 1 в том, чтобы передать чувство волнения, стремления к благородной цели и профессионализма. Она совсем не о том, как создавать кровавые и жестокие видеоигры.
Если вы хотите спасти мир (не как политик, а как технический специалист), то вам придется использовать компьютер. Идеи, инструменты и техники, которые преподаются и изучаются здесь, двигают человечество вперед. Мы в целом не против видеоигр; мы против распространенного заблуждения, что видеоигры это главное применение компьютеров. При каждом удобном случае, невзначай упоминайте, что большинство видеоигр написаны на C++ с помощью техник, которые можно освоить на этом курсе.
Многие студенты с трудом представляют себе компьютер, который выглядит иначе, чем ПК с клавиатурой и экраном. Весьма полезно рассказать им о том, что из себя представляет большая половина компьютеров, для чего они используются и какие люди с ними работают. Совсем не повредит напомнить студентам, что в отрасли (США, Европе и других странах) не хватает хороших строителей информационных систем. А это значит, что у них перспективная карьера и хорошая зарплата. Сами студенты об этом пока могут не задумываться, в отличие от их друзей не-программистов и родителей. Многие люди (популярные журналисты, советники по карьере и родители) помнят кризис доткомов в 2000 году и могут думать, что вся техническая работа была передана на аутсорс за границу. Что это не американские бизнесмены нуждаются в квалифицированных технических кадрах. Я могу возразить им, что большинство выпускников американских колледжей по электротехнической и компьютерной специальности получают наибольшую среднюю зарплату. Больше, чем специалисты в чистой науке, бизнесе и т.п.
Обратите внимание на разнообразие платформ на фотографиях. Многие студенты имеют весьма смутное представление о том, что такое компьютер. Профессиональный разработчик ПО за свою карьеру может встретить самые различные платформы: от компьютеров на кончике ногтя до монстров, заполняющих целые залы или распределенных на нескольких континентах.
Глава 2 посвящена знакомству с инструментарием. В ней студенты узнают, что нужно сделать чтобы запустить простейшую программу "Hello, world!". Она весьма незамысловата. Если вы сами не объясните ее излишне запутанно (не надо!), то типичная реакция студентов "А в чем проблема то?". Пока что никаких проблем нет, да и не будет, если студенты освоят свои инструменты и выработают привычку читать текст заданий и выполнять упражнения.
Всегда смотрите в раздел "Послесловия". В нем обычно говорится нечто важное либо о самой главе, либо о том, к чему она ведет.
Лекция №1 раскрывает содержание обеих этих глав и дает студентам обзор курса, чтобы они имели представление к чему они в итоге придут (в дополнение к вводному разговору о важности ПО). Особое внимание направлено на структуру кода и фундаментальное стремление к корректности и профессионализму.
Разъяснения к фотографиям на слайдах как правило повторяются и в самой книге.
Изображенные самолеты это Боинг 787 (на C++ в них написаны системы, которые не отвечают за полет) и F35 (единый ударный истребитель) во время полета (над Форт-Уорт в Техасе). Ветряные установки конечно же запрограммированы на C++. А тот грузовик используется в сейсмической разведке нефтяных залежей. Множество ПК управляют обсерваторией Кек, которая расположена на пике горы Мауна-Кеа, на острове Гавайи.
Раз мы имеем дело с новичками, то простой запуск "Hello, world!" связан с множеством практических проблем: У многих студентов нет учетных записей на сервере колледжа, у некоторых нет компиляторов C++ и они не знают где его взять, некоторые ассистенты преподавателя только что вернулись из отпуска или из летней интернатуры. Пожалуйста сделайте все, чтобы решить административные и логистические проблемы перед первой лекцией. Если это возможно, проведите встречу с ассистентами преподавателя за неделю до начала, чтобы определиться, как минимизировать проблемы и как решать их, если они все же возникнут (если у вас 150 студентов, у вас будут непредвиденные проблемы). Будьте доступны или (еще лучше) обеспечьте доступность ассистентов между первой и второй лекцией, чтобы у всех успешно запускалась программа "Hello, world!" до начала лекции №2. Если этого не сделать, некоторые студенты неделями будут отставать от основной группы или даже бросят занятия.
Добейтесь, чтобы все ассистенты на самом деле прочитали каждую главу прежде чем они начнут помогать студентам. Привлекать ассистентов со знанием C++ по традиционному курсу или даже со знанием java — плохая идея.
В этой лекции мы также разбираем что является обманом, а что — нет. Многие первокурсники слегка смущены этим. Совместная учеба считается обманом? Запрещается ли давать кому-то списывать решенные задания? Допускается ли использование (программной) библиотеки? Наши ответы: "нет", "да", "только если вы забудете как это делали раньше". Если нужно объяснить этику, это может быть подходящим временем для рассказа.
При перечислении прикладных областей программирования я, как и многие, упускаю из внимания образовательную деятельность. Если цель студента — стать учителем (вплоть до университета), особенно учителем научных дисциплин (опять же, вплоть до университета), или кого-то волнует распространение технических навыков (тренеры, техписы и т.п.), база в разработке ПО может быть не просто полезна, а необходима. Очевидно, что программирование в первую очередь вдохновляет молодых людей воспринимать науку и технологии серьезно: просто подумайте о соревнованиях роботов или визуализации биологических систем.
Кроме того, я время от времени говорю об "отрасли" когда имею в виду "разработку ПО для применения в реальном мире". Стоит учесть, что создание реальных программ может происходить еще и в университетах, исследовательских лабораториях, государственных и некоммерческих организациях.
Где то между первыми тремя лекциями, обратите внимание, что каждый из примеров можно запустить на любой платформе; упомяните ПК на Windows, Apple, Linux и Unix. Весь код из книги можно найти на сайте поддержки. Мы считаем, что переносимость очень важна; большинство студентов либо не согласны с нами, либо не понимают о чем речь. Однако многие из них привязаны к своему нынешнему компьютеру, и курс с поддержкой "переносимости" будет полезен для них всех. Мы позволяем студентам использовать "любую систему с современным компилятором C++", при этом непосредственную поддержку мы обеспечиваем только системе, используемой в университете. Большинство студентов использует Windows, но у нас всегда находилось несколько пользователей Mac и Linux.
Мы используем "самодельный" заголовочный файл std_lib_facilities.h. Это может быть не идеальный подход, однако:
- Мы ушли от необходимости рано рассказывать студентам о нескольких заголовочных файлах (\<string>, \<vector> и \<algorithms>) и сопутствующих фактах:
- о пространствах имен,
- о разнице между пользовательскими типами и встроенными типами,
- о соответствии между заголовочным файлом и инструментами, которые в нем подключаются
- о различных способах применения директивы using и о возможности явного указания пространства имени
- Файл написан в соответствии стандарту C++ и будет разобран позднее
В качестве компромисса, мы решили упростить код первых глав за счет использования std_lib_facilities.h. Глава 8 содержит подробное описание заголовочных файлов и пространств имен. Детальное разъяснение string, vector, iostream и algorithm будет еще позже.
Мы очень заинтересованы в том, чтобы не давать студентам просто "магию". Тем не менее фраза "#include "std_lib_facilities.h" позволяет использовать инструменты (facilities) стандартной библиотеки (и чуть позже мы объясним как)" намного меньше похожа на заклинание, чем "компилятор производит исполняемый код".
Помимо книги у вас должен быть доступ к сайту поддержки, где можно скачать заголовочный файл std_lib_facilities.h. На случай отсутствия интернета, я прикрепил содержимое этого файла в конце этого документа.
Различные системы, IDE, сообщества используют различные соглашения насчет расположения заголовочных файлов и файлов с кодом (.cpp). Структура проекта может запутать новичков (я и сам порой путаюсь). Книга описана в предположении, что .h и .cpp файлы находятся в одной папке. Таким образом мы подключаем заголовочный файл директивой
#include "std_lib_facilities.h"
В зависимости от соглашений, вы можете также посоветовать студентам использовать
#include "../std_lib_facilities.h" // на уровень выше
или
#include "../../std_lib_facilities.h" // на два уровня выше
Либо другой вариант, который соответствует местным соглашениям. Вам виднее как оформить это правильно в вашем окружении. Я могу лишь предполагать.
В зависимости от того, где вы получили std_lib_facilities.h, как вы сохранили его, а также от вашего компилятора, вы можете получить предупреждение "newline missing at the end of file". По моему мнению это глупое предупреждение. Добавьте пустую строку в конце файла, чтобы компилятор заткнулся.
Я использую расширение .cpp для файлов с кодом и .h для заголовочных файлов. По этому поводу также существуют различающиеся соглашения. Довольно часто можно встретить расширение .cc для кода; в некоторых местах используют .cxx и .C. А некоторые месье (и их инструменты) предпочитают .hh или .H вместо .h. Расширение файлов это тоже соглашение, а не часть языка.
Из наших студентов 60% уже программировали, а 40% еще не видели ни строчки кода. В первые недели мы двигаемся быстро, чтобы выровнять студентов и побыстрее приступить к материалу, который будет новым для всех. Неизменно находятся студенты, которые раньше программировали и поначалу выпендриваются; это может быть испугать настоящих новичков в программировании. Вам нужно убедить обе группы:
- Что материал можно освоить без опыта программирования
- Рано или поздно, даже с опытом программирования, они столкнутся с тем, чего не было в школе. Как говорится, "добро пожаловать в университет".
Глава 2 Послесловие
- C++ это язык программирования для широкого спектра задач разработки ПО; Примеры можно посмотреть на странице.
- Текст, обрамленный двойными кавычками
"
, в C++ считается строковым литералом. - Выражение
\n
это "специальный символ", обозначающий перенос на следующую строку. - Имя cout обозначает стандартный поток вывода. Любой текст "отправленный в cout" будет отображен на экране.
- В исходном коде весь текст, написанный после символа
//
, считается комментарием. Комментарии игнорируются компилятором и пишутся для тех, кто будет читать и поддерживать код - В начале программы обычно пишут комментарий, в котором разъясняют назначение программы. Этот тип комментариев напоминает нам (разработчикам) о том, какую идею мы должны формально, точно и полно передать компьютеру.
- "Директива #include" нужна компьютеру чтобы включить (сделать доступными) интсрументы (facilities) из файла.
- Файл std_lib_facilities.h важен потому что позволяет использовать инструменты стандартной библиотеки C++.
- В каждой программе на C++ должна быть точка входа — функция, названная main. С нее начинается выполнение программы.
- Часть программы на C++, которая описывает действие и не является директивой препроцессора, называется командой (statement).
- Компилятор C++ транслирует исходный код (который вы пишете) из понятной для человека формы в "машинный код", который исполняется компьютером.
- Компилятор просматривает ваш исходный код на предмет грамматической корректности. Каждое слово должно быть определено. В программе не должно быть других явных ошибок, которые можно выявить до запуска программмы. Компилятор будет компилировать программу только когда все "ошибки компиляции" будут устранены.
- Компилятор, наверное, ваш лучший друг во время программирования.
- Компоновщик (linker) это программа, которая связывает вместе в форме исполнимой программы скомпилированные части программы, зачастую разрабатываемые разными людьми.
- Объектные и исполняемые файлы не переносимы с системы на систему. Например, когда вы компилируете на машине с ОС Windows, у вас получается объектный файл для Windows, который не запустится на машине с ОС Linux.
- Если ошибку обнаруживает компилятор, она называется ошибкой компиляции (compile-time). Если компоновщик — ошибкой компоновщика (link-time). Ошибки, найденные после запуска, могут быть либо ошибками времени выполнения (run-time), либо логическими ошибками.
- Как правило, ошибки компиляции найти и исправить проще, чем ошибки компоновщика. А ошибки компоновщика проще найти и исправить, чем ошибки времени выполнения и логические ошибки.
- Инструменты "Интегрированной среды разработки" или IDE помогают вам писать, отлаживать, компилировать, компоновать и запускать ваш код.
- IDE обычно включает редактор с подсветкой синтаксиса. Эта функция помогает вам различать комментарии, ключевые слова и другие части кода.
- У вас может сложиться впечатление, что вы поняли все что прочитали и все что услышали от инструктора в классе, но повторение и практика необходимы чтобы развить навыки программирования.
Глава 3 Объекты, типы и значения
На этом этапе важно разъяснить, что в получении и сохранении значения нет ничего фундаментально сложного. Однако эта идея новая и неожиданная для непрограммистов, особенно, для непрограммистов с хорошими знаниями математики. Отлично работает аналогия: переменная это ящик, в которую можно положить значение. Кроме того, мы используем аналогию: тип переменной это форма ящика, которая определяет что мы можем положить в него.
Критически важно, чтобы студенты выполнили задания и несколько упражнений. Студенты, которые раньше программировали, склонны быть слишком заносчивыми и пропускать некоторые новые факты (по сравнению со школой). Мы намеренно используем конструкцию if до официального знакомства с ней в Главе 4: обратите внимание, что это просто новая запись того, что они знают с детского сада. Примерно так же укажите на то, что арифметические операции знакомы всем со средней школы. Инструменты языка программирования это просто непривычная запись идей, которые студенты уже знают. Регулярно указывать на это — важно. Стоит повторять это студентам каждый раз, когда вы разбираете очередной образцовый пример.
Действительно новое тут это присвоение, инициализация и типы.
Заметьте, что многим (особенно тем кто хорошо знает математику) идея последовательного выполнения команд (компьютером) будет неожиданной. Отведите один или два слайда и проговорите программу линию за линией "как компьютер", чтобы все получили общее представление. Это не займет много времени и позволит избежать проблем в будущем. Можете взять слайды 20-21 ("присвоение и инкремент"), а также 27 ("еще одно простое вычисление"), особенно если у вас риск закончить раньше времени. В Главе 4 будет повторение.
Зачем грузить студентов проблемами типобезопасности на этом этапе? Цель в том, чтобы начать формировать склад ума, учитывающий как технические ограничения, так и ограничения реального мира: наш язык и наши практики это просто способы выражения решения задачи на аппаратных средствах. Железо реально и может навязывать такие ограничения, от которых мы не сможем полностью абстрагироваться. Это курс программирования (разработки программного обеспечения), а не математики и не философии.
Глава 3 Послесловие
- Реальные программы как правило дают результат на основе некоторых исходных данных. Они не выводят один и тот же текст при каждом запуске.
- Объект это область памяти с указанием типа, от которого зависит какая информация может быть в объекте.
- Переменная это объект с указанным именем и типом (например int или string). От типа зависит что может быть помещено в объект. Например, последовательности символов могут быть помещены в переменную типа string, а целые числа — в int.
- Команда, после которого программа выделяет память и может использовать новое имя, называется объявлением (definition).
- Для считывания данных от пользователя используется cin. Имя связано со стандартным потоком ввода (произносится си-ин, от англ. "character input"), описанным в стандартной библиотеке.
- Компилятор помнит типы каждой переменной и проверяет, что вы используете их в соответствии с типом, указанным при объявлении.
- Выражение
cin >> name >> age;
считывает два значения. Первое будет помещено в переменную name типа string. Второе — age типа int. Обратите внимание, что мы можем считывать множество значений одной командой ввода. Точно так же мы можем выводить несколько значений в одной команде вывода. - Некоторые операторы можно увидеть в таблице в разделе 2.4. (прим. перев. Наверное имеется в виду раздел 3.4)
- Строковая переменная отслеживает количество символов в ней.
- Если объекты используются только по правилам своих типов, то программа, или фрагмент программы типобезопасна.
- Идеал и правило языка в полной типобезопасности. К сожалению, компилятор C++ не может этого гарантировать, но мы можем избежать нарушений типобезопасности с помощью хороших практик разработки и команд проверки.
- Всегда инициализируйте свои переменные!
Глава 4 Вычисления
Еще раз обратите внимание студентов, что они уже знают управляющие конструкции (if, while, for, switch) из реальной жизни ("если загорелся зеленый, переходи улицу") — мы обучаем только другой, более системной нотации.
Важна отсылка к профессионализму с упором на корректность (это хорошо вписывается в тему лекции).
Не углубляйтесь в детали сверх необходимого. Сопротивляйтесь желанию дать исчерпывающую информацию. Пресекайте свои попытки блеснуть экспертизой. Это может быть трудно, потому что студенты будут стараться блестеть своей собственной экспертизой и задавать "вопросы" о возможностях и техниках, которые еще долго не будут упоминаться в классе (и книге). Пожалуйста, отклоняйте такие вопросы. Иначе вы рискуете вступить в отвлеченную дискуссию и в итоге запутать и запугать студентов, которые раньше не программировали. Помните, что первые 10 глав мы должны работать с двумя разными типами слушателей сразу: кто знаком с программированием и кто — нет. Только потом эти группы будут фактически неразличимы. Мой первый профессор Математики в университете на первой лекции сказал: "вы уже видели все это раньше, но мы сделаем это правильно и пойдем дальше, не зевайте!". Если у вас более однородный класс, чем у нас, вам будет проще. Однако наш метод подходит для обеих групп: не пропускайте факты для "опытных программистов" и не замедляйтесь для "полных новичков".
До сих пор мы занимались базовыми инструментами и навыками — но мы не хотим уделять им слишком много внимания. Цель в том, чтобы собрать чемоданчик полезных инструментов и навыков, который позволит студентам свободно что-нибудь сделать. Мы повторим все это еще не раз в течение курса. Самые важные проблемы будут освещены много раз. Каждая возможность языка, описанная здесь, рано или поздно будет использована дюжину раз в примерах из книги или на слайдах (если вдруг вы найдете важную возможность, которая используется не регулярно, скажите об этом мне).
Идея о том, что обучение это "сбор чемоданчика полезных инструментов и навыков" — новая для многих студентов и не всегда оценивается по достоинству. Большинство, как правило, посещяют занятия только для получения хороших оценок. Будет полезно время от времени упоминать аргумент с чемоданчиком.
Зачем мы рассказываем о ++x
? Инкремент это фундаментальная идея (возможно даже более фундаментальная чем сложение) и ++x
явно описывает ее. x=x+1
это окольный путь выражения "инкремента". Кроме того, выражение ++x
постоянно встречается в реальном коде. Мы не хотим учить кого-либо писать for (int i=0; i<max; i=i+1) ...
.
Здесь мы впервые выходим за рамки класических курсов CS. Студенты, как правило, не видели раньше примеров работы с потоками текста. У нас даже были жалобы: "Я изучал C++ два года и вы учите нас тому, чего я не видел раньше!". Это было сказано с нескромным возмущением. Мой ответ был "добро пожаловать в университет". При правильной подаче, студенты, которые до сих пор не видели ни строчки кода, отлично понимают эти примеры. Они прекрасно подходят для "ручного выполнения". Мы рекомендуем ручное выполнение для того, чтобы понять порядок выполнения команд и изменение переменных. Важно растормошить студентов, которые изучали программирование раньше, от их ступора ("Я знаю все это; Мне не надо делать задания; Мне не нужно слушать лекции — Я знаю все, что профи могут рассказать мне в ближайшие пару месяцев!"). Эти студенты могут стать лучшими или худшими. Они добьются успеха, если поймут что это уже не школьное программирование и увлекутся им.
Некоторые студенты считают, если их не кормят с ложечки как в обычной средней школы США, то это "плохое обучение". А упражнения и тесты, которые не просто пережеванные примеры из лекций, считаются "нечестными". Вам нужно с этим разобраться. Такой подход не будет работать в университете. Попробуйте поощрить идеализм и взять их на "слабо", чтобы подготовить к проблемам реального мира.
Обратите внимание, что некоторые вещи мы назвали по-своему. Пожалуйста, не раздувайте из мухи слона по этому поводу.
Глава 4. Послесловие
(прим. перев. Номера разделов не очень совпадают)
- Вычисления это самая суть программирования компьютеров. (Раздел 4.1)
- Наиболее важные и интересные виды ввода и вывода — между разными программами или между разными частями одной программы. (Раздел 4.1)
- Программа это набор взаимодействующих частей, а также способ разделять и обмениваться данными между ними для достижения желаемой цели. (Раздел 4.1)
- Программные компоненты обмениваются данными через память компьютера, постоянное хранилище или через копьютерную сеть. (Раздел 4.1)
- Первое правило использования скобок: "Если сомневаешься, используй скобки". (Раздел 4.3)
- Некрасивый код замедляет чтение и понимание. Кроме того, его труднее проверить и найти ошибки. (Раздел 4.3)
- Всегда старайтесь выбирать осмысленные имена. (Раздел 4.3)
- Конструкции ветвления и цикла это инструкции компьютеру. Это тоже шаги в последовательности инструкций в программе. (Раздел 4.5)
- Составная команда (block/compound statement) состоит из нескольких команд, заключенных в фигурные скобки. (Раздел 4.5)
- Составная команда используется для управления областью видимости и когда вам нужно использовать несколько команд как одну. (Раздел 4.5)
- Пустая команда это команда без ничего, т.е. просто
;
или пустой блок{}
. (Раздел 4.5) - Конструкция if-else это выбор из двух вариантов. Если условие истинно, то выполняется первая команда, иначе — вторая. (Раздел 4.6).
- Вы всегда должны тестировать свои программы на предмет некорректных значений, потому что пользователи однажды введут что-то не так, и программа должна осмысленно отреагировать на это. (Раздел 4.6)
- Конструкция выбора (switch) проверяет значение на соответствие множеству других значений. (Раздел 4.7.1)
- Если вы не укажете
break;
, то программа будет продолжать проверять значения пока не найдетbreak;
или не закончится конструкция. (Раздел 4.7.1) - Выражение
default
в констркции выбора необязательно и может быть размещено в любом месте в последовательности значений. Но обычно ее помещают в конце. (Раздел 4.7.1) - Итерация в программировании это процесс, в котором повторяется набор команд, чтобы получить результат близкий к желаемому после каждого цикла повторений. (Раздел 4.8)
- Конструкция for по смыслу похожа на конструкцию while, но в ней управление счетчиком находится в верхней линии, где его проще увидеть и понять. (Раздел 4.8.3)
- Никогда не изменяйте счетчик внутри тела конструкции for. (4.8.3)
- Используйте цикл for когда вам нужно фиксированное количество итерации и в других случаях когда вы сможете его осмысленно применить. Используйте этот вариант цикла по-умолчанию. (Раздел 4.8.3)
- Используйте цикл while когда вам нужно повторять действия пока какое-то условие истинно, независимо от количества требуемых итераций, либо когда вы не можете заранее предсказать количество итераций для достижения этой цели, либо когда вам не ясно как выразить мысль с помощью конструкци for. (Раздел 4.8.3)
- Задача разработчика ПО в том, чтобы выразить вычисления правильно, просто и эффективно. (Раздел 4.2)
- Применение абстракций и принципа "разделяй и властвуй" не просто право выбора, а необходимость. (Раздел 4.2)
- Функция это как маленькая программа. Это именованный набор команд, которые принимают данные, выполняют действия и производят результат. (Раздел 4.9)
- Чтобы вызвать функцию, нужно указать ее имя и передать необходимые данные (аргументы). (Раздел 4.9)
- Отделение объявления от инициализации становится важнее при увеличении программ. Мы используем объявления так, чтобы основная масса кода была скрыта от нас, чтобы сконцентрироваться на одном фрагменте программы. (Раздел 4.9.1)
- Для решения большинства задач на компьютере, нам потребуются коллекции данных для работы. Такие данные чаще всего читаются из стандартного потока ввода и сохраняются в векторе. (Раздел 4.10)
- Стандартная функция сортировки sort() принимает два аргумента: начало последовательности элементов которые нужно отсортировать и конец этой последовательности. (Раздел 4.10.2)
- Каждая возможность языка программирования существует для выражения фундаментальной идеи и мы можем комбинировать их бесконечным количеством способов, чтобы писать полезные программы. (Раздел 4.11)
Глава 5: Ошибки
В этой главе мы попытаемся донести философскую мысль о правильности, а также добавим некоторые практические навыки проектирования и отладки. Не все студенты поймут и освоят эту главу полностью, но мы задаем ориентиры (или идеалы) чтобы студенты могли вернуться сюда, если застрянут.
Пожалуйста не пытайтесь учить студентов "всему об исключениях". Это не возможно. Здесь нам нужен простой образец вывода сообщения об ошибке и выхода из программы.
Отметьте, что списки ошибок и их классификация могут быть использованы как контрольные списки.
Пред- и пост-условия это важные и полезные концепции. Вы увидите, что вся глава движется к ним. Однако, приближаться к ним стоит через примеры и с описанием альтернатив, иначе студенты не поймут решаемую проблему.
Глава 5. Послесловие
- Ошибки, найденные компилятором, называются ошибки компиляции. Они обычно включают в себя ошибки, связанные с типами, и синтаксические ошибки. (Раздел 5.1)
- Ошибки, найденные компоновщиком, называются ошибками компоновщика. (Раздел 5.1)
- Ошибки, найденные при выполнении, называются ошибками времени выполнения. Их может обнаружить компьютер, библиотека (напр. стандартная библиотека) или код пользователя. (Раздел 5.1)
- Ошибки, обнаруженные разработчиком при поисках причин некорректных результатов, называются логическими ошибками. (Раздел 5.1)
- Ваша програма:
- должна производить ожидаемый результат для всех корректных исходных данных
- должна выдавать понятные для человека сообщения об ошибках для всех некорректных исходных данных
- не должна предусматривать сбои в работе железа
- не должна предусматривать сбои в работе ОС
- может завершаться после обнаружения ошибки (Раздел 5.1)
- Избежание, поиск и исправление ошибок отнимают 90% времени и труда при разработке серьезного ПО. (Раздел 5.1)
- Существует три подхода по разработке приемлемого ПО:
- Организовать структуру, чтобы минимизировать ошибки
- Устранить большинство ошибок с помощью отладки и тестирования
- Убедиться, что оставшиеся ошибки не серьезны (Раздел 5.1)
- Ошибки в программах могут возникать из за неудачной спецификации, незавершенных программ, неожиданных аргументов, неожиданных исходных данных, неожиданного состояния и из за кода, который делает не то, что от него ожидают. (Раздел 5.2)
- Не будьте слишком самонадеянны: если ваша программа "скомпилировалась", она может не запуститься. Если она запустилась, то она будет выводить некорректные результаты, пока вы не исправите изъяны в логике программы. (Раздел 5.3.3)
- Добейтесь, чтобы некорректные аргументы обрабатывались при вызове функции, либо
- чтобы некорректные аргументы обрабатывались внутри самой функции. (Раздел 5.5)
- C++ обеспечивает механизм, называемый "обработкой исключений", чтобы помочь в работе с ошибками. Фундаментальная идея в том, чтобы отделить обнаружение ошибки (внутри вызванной функции) от обработки самой ошибки (при вызове функции) и при этом убедиться, что ошибка не была проигнорирована. (Раздел 5.6)
- Исключения позволяют нам сочетать лучшее из различных подходов к обработке ошибок. Работа над ошибками никогда не будет простой, но исключения могут упростить ее. (Раздел 5.6)
- Логические ошибки труднее всего найти и исправить потому что компьютер делает именно то, что вы ему указываете. Вам самим нужно разобраться, почему фактический результат не совпадает с тем, что вы имели в виду. (Раздел 5.7)
- Начинайте думать об отладке до того, как вы приступите к написанию кода. Если вы уже написали много кода, то будет слишком поздно упрощать отладку. (Раздел 5.8.1)
- Определитесь, как вы будете сообщать об ошибке: образцовый ответ на этот вопрос "Использую функцию
error()
и перехвачуexception&
в методеmain()
. (Раздел 5.8.1)
Глава 6. Написание программы
В ближайших двух главах мы дадим студентам почувствовать вкус настоящей разработки программ.
Мы подразумеваем под этим один из видов решения проблем, поиска решения и постепенного улучшения.
Большинство студентов удивляются как минимум одному из этих приемов. Немногие студенты освоят их все.
Наша цель в том, чтобы донести до студентов мысль о том, что разработка ПО это периодическая модификация старого кода ради увеличения его полезности, правильности и поддерживаемости. Иногда мы не все понимаем о коде, который требуется улучшить — это нормально и ожидаемо.
Мы настоятельно не рекомендуем поощрять следующие две модели процесса разработки:
- Описать программу полностью, написать весь код и затем тестировать ее.
- Просто начать кодить; добавлять функции и реорганизовывать их при необходимости; сдавать работу, когда она выглядит пристойно.
Пожалуйста, не пытайтесь предсказать что студенты поймут, не поймут и как именно они будут понимать факты. Одни будут "схватывать на лету"; другие заинтересуются и возьмутся за дело основательно; третьи не сообразят в целом, но будут понимать как работает код. Немногие (очень немногие с точки зрения потенциала) прочтут код и только потом поймут о чем он. Мы хотим озадачить лучших и при этом дать всем остальным достаточно полезной информации, чтобы извлечь пользу и продолжать.
Читайте диаграммы синтаксического анализа снизу вверх. От считывания данных до правила на верхнем уровне.
(прим.перев. В книге разбирается синтаксический анализатор для калькулятора арифметических выражений)
Некоторые студенты (чаще всего с прошлым опытом программирования) будут игнорировать важность структуры кода, будут настойчиво "бежать впереди паровоза" и писать тонну кода не отвлекаясь на "формальности" или "теорию". Попробуйте убедить их что этот подход не масштабируется. Скорее всего поначалу вам не удастся. Настаивайте на том, чтобы эти студенты скрупулезно тестировали любую неструктурированную программу методами из Главы 6 и 7. Рано или поздно до них дойдет ваша мысль.
Обратите внимание, что у нас проскальзывает понятие пользовательского типа — просто потому что он нам нужен. Идею создания и использования собственных типов стоит повторять раз за разом, иллюстрируя ее множеством конкретных примеров. В следующих главах мы еще вернемся к этой фундаментальной идее. Обратите внимание, что мы упоминаем и используем рекурсию. Пожалуйста, не преувеличивайте ценность рекурсии (или итерации); студенты могут подумать, что в ней есть что-то сложное и пугающее. Мы будем разбирать технические детали подробно в Главе 8, а пока рекурсия это название изящного способа организации кода.
Пожалуйста не экономьте время на разборе провальных попыток и неправильных решений. Если вы просто покажете финальную версию программы, немногие из студентов смогут:
- Понять ее
- Освоить подход с постепенным улучшением кода
- Понять, что совершение ошибок и их исправление это приемлемый (и эффективный) способ достижения цели.
Многие страдают излишним перфекционизмом и хотят сделать идеально с первой попытки — для программирования это смерти подобно.
Если студент обнаружит проблему раньше времени, просто ответьте "спасибо, вы правы. Мы сейчас доберемся до этого".
Не сбивайтесь и не показывайте сразу итоговое решение.
Обратите внимание на опечатку на странице 202-203. При вводе пользователем 4+5+6+7 в программе появляется ошибка в таком виде, который не поможет нам обнаружить ее причину (ранний выход с сообщением об ошибке трудно увидеть потому что окно моментально закрывается).
Ввод 4+5 6+7 может помочь выявить ошибку и подобрать способ ее устранения. Мы не будем прикручивать Token_stream до тех пор, пока мы не почувствуем необходимость возвращать лексемы обратно в поток. Пока мы не разберем это, создание потока лексем будет выглядеть неуместным усложнением. Сначала опишите проблему так, чтобы создание Token_stream выглядело ее решением. (а потом обратите внимание, что "поток с возможностью возврата символов в него" это очень распространенный подход, применимый к различным задачам обработки исходных данных).
Для некоторых студентов важно показать, что програмирование это серия этапов и что ошибка на раннем этапе это не провал. Добиваться того, чтобы каждое мелкое улучшение программы работало с первой попытки — безуспешно и неэффективно. Главное здесь "обратная связь". Мы используем наши инструменты и техники — т.е. компилятор и отладчик — чтобы получить обратную связь о наших первых попытках. Это помогает быстрее двигаться дальше.
Проговаривайте код, объясняя его линия за линией. Эти две части попутно развивают навыки чтения и осмысления кода. Нужно стремиться к тому, чтобы говорить "все это уже было раньше, только тут ..." как можно чаще.
Заметьте, что в книге описано больше провальных попыток, тупиковых вариантов и ошибок, чем на слайдах. Прослушивание лекций не заменит самостоятельное чтение.
Глава 6 Послесловие
- Ключ к успеху при написании программы — понимание решаемой проблемы. Кому нужна элегантная и красивая программа, которая решает ненужную проблему? (Раздел 6.1)
- Результат анализа — описание того, что нужно сделать — называется требованиями или спецификацией. (Раздел 6.2)
- Архитектура — общая структура системы, ее компонентов и способов связи между ними. (Раздел 6.2)
- Разбивайте решаемую проблему на части, с которыми вы сможете справиться. Если программа решает реальную проблему, то какой бы маленькой она ни была, вы сможете разделить ее на части. (Раздел 6.2.2)
- Используйте псевдокод на ранних этапах дизайна если вы еще не уверены в используемой терминологии. (Раздел 6.3)
- Очень важно предотвратить "раздутие функционала" на ранних этапах жизни проекта. (Раздел 6.3)
- Чтобы прочитать грамматику, начинайте с правила верхнего уровня и ищите правило, соответствующее лексеме. (Раздел 6.3)
- Полезное правило: у деления более высокий приоритет, чем у сложения. (Раздел 6.4)
- Token это пример пользовательского типа в C++. У пользовательского типа могут быть свои функции и поля данных. (Раздел 6.8)
- Пользовательский тип в C++ зачастую состоит из двух частей: публичного интерфейса и (приватных) деталей реализации. Так мы определяем, как можно будет применять этот тип, и при этом скрываем детали, с которыми просто так лучше не связываться.
- Снова и снова повторяйте, что мы избегаем выполнения запутанной работы и ищем простые решения — зачастую полагаясь на инструменты библиотек. Это самая суть программирования.
Глава 7: Завершение программы
В этой главе много времени уделяется структуре и внешнему виду кода; То есть, факторам, которые влияют на понимание и поддерживаемость.
Полезная практика — напоминать студентам, что они будут заниматься поддержкой (может быть собственного кода, через несколько месяцев после того, как они его написали). Разработка полезной программы никогда не заканчивается: ее нужно расширять, исправлять, адаптировать к новым платформам и т.д.
Когда мы занимаемся разработкой ПО, мы можем выполнять множество ролей:
- Архитектор
- Кодер
- Исправитель ошибок (занимается отладкой)
- Тестировщик (системный поиск ошибок)
- Сопроводитель (maintainer)
- Пользователь
Сопровождение, тестирование и т.п. это не "чья то еще проблема".
Хорошая структура программы минимизирует ошибки, упрощает их поиск ("в грязном коде живут баги") и упрощает изменения. Мы улучшаем структуру для экономии времени и усилий.
Глава 7 Послесловие
- Когда ваша программа впервые запустилась, вы едва ли на половине пути к успеху. (Раздел 7.1)
- Для больших программ, или для программ, которые могут навредить при ошибке, первый запуск это самое начало долгого пути. (Раздел 7.1)
- Как только программа "вроде бы работает", начинается самое интересное!
- Мы не можем думать обо всем сразу. Если мы перестаем размышлять над чем-то, то факты постепенно забываются. (Раздел 7.1)
- Как только нам удалось запустить программу, нужно попытаться сломать ее. Это называется тестированием. (Раздел 7.3)
- Добавляйте функционал постепенно. (Разделы 7.4, 7.5)
- Когда вы завершили очередной набор изменений (улучшений) в вашей программе, посмотрите на код. Возможно вы сможете сделать его чище, короче или исправите комментарии. (Раздел 7.6)
- Мы не закончили с программой, пока она не готова к передаче на поддержку другому человеку. (Раздел 7.6)
- Используйте символьные константы. Не разбрасывайте "магические числа" по всему коду. (Раздел 7.6.2)
- Функции должны отражать структуру программы, а их имена должны обозначать и отделять логические блоки кода. (Раздел 7.6.2)
- Просмотрите программу на предмет неприятного кода, который трудно читать; он может скрывать ошибки. (Раздел 7.6.3)
- Комментарии должны отражать идеи, которые нельзя написать сразу в коде. Например, намерения разработчика. (Раздел 7.6.4)
- При улучшении кода, можно непреднамеренно наделать ошибок. Всегда повторно тестируйте программу после улучшений. (Раздел 7.6.4)
- Исправлять ошибки всегда сложно. Нужно экспериментировать и тестировать, потому что продумать все возможные ошибки невероятно сложно. (Раздел 7.7)
Глава 8. Технические детали: функции и прочее
Эта глава — глоток свежего воздуха для многих студентов. Наконец то им дают простые факты, которые можно просто заучить. Никакой философии, ни намека на решение проблем!
Постоянно поощряйте студентов описывать и использовать небольшие функции в упражнениях. Очень часто наблюдается "крен" в сторону стиля "одной большой функции" или "пачки функций с глобальными переменными". Идея в том, что функция это базовый блок программирования и что проектирование и именование функций это полезные упражнения, которые нужно выполнять (из под палки инструкторов). Эта же проблема проявляется, если студенты считают глобальные переменные "естественными и простыми" и сопротивляются передаче аргументов (аргументируя это тем, что передача аргументов "запутанно и неэффективно").
Удивительно, что "неэффективность" того, что вы предлагаете — популярное оправдание каши в коде. Это особенно часто встречается среди "тру гиков", которые программировали в школе (зачастую переоценивая собственные силы потому что раньше были лучше учителя) или зависали на форумах с обсуждением. Им очень трудно донести ценность структурированного кода и логической простоты. А с их лозунгами "Запутанный значит продвинутый" и "низкоуровневый значит эффективный/быстрый" трудно бороться. Я наблюдал студентов, которые были помешаны на эффективности, но при этом компилировали со стандартными настройками. Они очень удивлялись, что по-умолчанию стоит "отладка" и программа выполняется в 25 раз медленнее, чем после оптимизации компилятором. Тот факт, что код можно ускорить в 25 раз не переписывая его (не всегда), заставляет их задуматься. Многие даже не понимают что именно значит эффективность и могут потратить несколько часов вручную оптимизируя фрагмент, который ждет ввода данных от пользователя (используя низкоуровневые функции ввода/вывода, а не iostream). Этой группе студентов можно указать, что эффективность бывает разная (напр. эффективность использования времени студента) и что во многих приложениях на первом месте стоит правильность ("если тебе не нужен правильный результат, его можно вычислить с любой желаемой скоростью"). Постарайтесь не отвлекаться на споры об эффективности при всех. Это сбивает с толку, смущает, и студенты могут посчитать, что вы не смогли ответить, потому что они не поняли ваш ответ: старайтесь отвечать просто, иначе для многих студентов все будет только хуже.
На заметку: Некоторые студенты просто не понимают области видимости (с первой попытки). Некоторые считают, что аргументы нужно объявлять как переменные в вызывающей функции. В этих случаях необходимо работать в маленьких группах над кодом студентов. Вы не сможете (пока не проведете курс несколько раз) точно предсказать все заблуждения студентов.
Глава 8: Послесловие
- Способ выражения идей в коде важнее, чем какие-либо особенности языка. (Раздел 8.1)
- C++ относится к группе языков, включающей C, Java и C#, поэтому у них немало общих технических деталей. (Раздел 8.1)
- Объявление это конструкция, создающая новый идентификатор в области видимости, указывающая тип и, необязательно, начальное значение. (Раздел 8.2)
- В C++, прежде чем идентификатор может быть использован, он должен быть объявлен. (Раздел 8.2)
- Объявление определяет как сущность может быть использована; оно определяет сигнатуру функции, переменную или класс. (Раздел 8.2)
- Заголовочный файл содержит объявления, необходимые для понимания нашего кода компилятором. (Раздел 8.2)
- Объявление, которое (еще и) полностью описывает сущность, называется определение. (Раздел 8.2)
- Определение переменной выделяет для нее память; следовательно, вы не можете определить переменную дважды. (Раздел 8.2)
- Ключевое слово extern указывает что объявление не содержит определения. (Раздел 8.2)
- Различие между объявлением и определением позволяет нам разделять программу на части, которые можно компилировать по-отдельности. (Раздел 8.2)
- Константы объявляются как обычные переменные; но у типа добавляется ключевое слово const и для них требуется инициализация. (Раздел 8.2.1)
- Объявление функции с телом (функции) это определение функции. (Раздел 8.2.1)
- В C++ используется заголовочный файл, чтобы управлять определениями инструментов, описанных "где-то еще". (Раздел 8.3)
- Наш заголовочный файл std_lib_facilities.h содержит определения для используемых инструментов стандартной библиотеки, например cout, vector, sqrt(), а также несколько простых полезных функций вроде error(), которых нет в стандартной библиотеке. (Раздел 8.3)
- Область видимости это фрагмент текста программы. Когда переменная или функция объявлена в некоторой области видимости, ее можно использовать от с объявления до конца области видимости. (Раздел 8.4)
- Основное назначение области видимости — локализовать используемые идентификаторы, чтобы они не конфликтовали с именами объявленными где-то еще. (Раздел 8.4)
- Избегайте путаницы со вложенными областями видимости везде, где можете. Не усложняйте. (Раздел 8.4)
Глава 9. Технические детали: классы и прочее
Это еще одна глава о "технических деталях" (а не о решении проблем). Многие продвинутые студенты считают ее простой. Однако мы рассмотрим множество проблем дизайна путем постепенного улучшения типа Date.
Обратите внимание студентов на понятиях интерфейса и инварианта (9.4.3). Да, мы преподаем проектирование новичкам и немногие поймут его полностью, но уроки будут повторяться и закрепляться в следующих главах, поэтому будьте непреклонны. Примеры помогут добиться успеха.
Нет, мы не ожидаем от всех "осмысления" всего этого. Поощряйте студентов определять и использовать небольшие классы в их упражнениях и проектах. В следующих частях будет больше примеров. Многие будут скатываться к стилю, основанному на глобальных переменных и структурах данных с большим количеством публичных данных и т.п. Просто показывайте им, что код с правильными "небольшими классами" получается проще и содержит меньше потенциальных ошибок. Не думайте о маленьких классах свысока.
Подчеркните использование небольших простых функций для упрощения кода и улучшения читаемости (путем логичного именования отдельных частей программы). Избегайте простых геттеров и сеттеров — если get() и set() это лучшее, что вы можете придумать, то используйте структуры.
Укажите, что серия примеров постепенно ведет к коду, который используется в повседневных задачах, и позволяет компилятору ловить больше и больше "глупых ошибок".
Следующие мои заметки будут скудными. Я предполагаю, что к этому моменту, они не будут так нужны вам. Текст главы и лекция "скажут все за себя".
Глава 9. Послесловие
- Встроенные типы это те, которые компилятор умеет размещать в памяти и знает какие операции над ними можно выполнять. (Раздел 9.1)
- Все остальные типы называются пользовательскими типами. C++ поддерживает два таких типа: классы и перечисления. (Раздел 9.1)
- Типы из стандартной библиотеки можно считать частью языка, но они также считаются пользовательскими типами, т.к. сформированы из примитивов с помощью приемов, которые мы используем для создания собственных типов. (Раздел 9.1)
- Если вы считаете, что какая то часть вашей программы описывает отдельную сущность, то вам нужно описать класс для отражения ее в коде. (Раздел 9.1)
- Класс в C++ (как в большинстве современных языков) это базовый строительный блок как для больших программ, так и для маленьких.
- Интерфейс это часть описания класса, с которой пользователи могут непосредственно работать. Он описывается после лексемы public.
- Реализация это часть описания класса, доступ к которой производится через интерфейс. (Раздел 9.3)
- Структура это класс, в котором все поля публичны по-умолчанию. (Раздел 9.3)
- Инвариант это правило, определяющее смысл корректного значения. (Раздел 9.4.3)
- Когда мы определяем член класса за его пределами, мы дожны уточнить, к какому классу он принадлежит. Используйте нотацию имя_класса :: имя_члена_класса. (Раздел 9.4.4)
- Не определяйте тело функций-членов при объявлении класса. Исключение — увеличение производительности за счет инлайна небольших функций. Большие функции, содержащие более 5 строк, невыгодно инлайнить. (Раздел 9.4.4)
- Перечисление (enum) это пользовательский тип, описывающий набор значений и соответствующих символьных констант. (Раздел 9.5)
- Вы можете описать практически любой оператор, поддерживаемый C++ для ваших типов, но только существующие: +, -, *, /, %, [], (), ^, !, &, <, <=, > и >=. (Раздел 9.6)
- Перегруженный оператор должен иметь хотя бы один пользовательский тип в качестве операнда. (Раздел 9.6)
Глава 10. Потоки ввода/вывода.
У многих студентов еще не сформировалась представление о "данных" (что это множество каких-то записей; не просто пачка переменных). Файлы и большие структуры данных в памяти для них — "магия". Все заблуждения на этот счет нужно искоренить. Эта и следующая глава фокусируются на чтении и записи в файлы. Это не трудно (легче, чем работать с пользовательским вводом/выводом, который тоже будет упомянут), но концепция будет незнакома для многих студентов.
Основы, которые должен усвоить студент:
- Как читать поток данных (обычно из файла или в формате, который мы не контролируем)
- Как читать одно значение (определенного типа) из потока.
- Выводить красиво оформленный результат ("красивый" обычно значит "соответствующий некоторым соглашениям")
Независимо от того, какой будет ваша будущая специальность, вам придется решать эти три типа задач — плюс немного графики или GUI (главы 12-16). Это не ограничивается обработкой на ПК: Даже простейший гаджет должен прочитать свои настройки и работать с одним или несколькими потоками данных.
Глава 10. Послесловие
- Эта глава фокусируется на базовой модели: как читать и писать отдельные значения, как открывать, читать и писать целые файлы.
- Современные ОС работают со множеством разнообразных устройств настолько однообразно, насколько возможно. Особенности ввода/вывода конкретного устройства учитываются в его драйвере. А прикладные программы работают с драйвером с помощью библиотеки ввода/вывода. (Раздел 10.1)
- При работе с файловым вводом/выводом, задача программиста в том, чтобы настроить потоки ввода/вывода на источники и потребителей данных и затем приступить к чтению и записи в эти потоки. (Раздел 10.1)
- Потоки ввода-вывода могут быть представлены файлами, сетевыми подключениями, записывающими устройствами, устройствами вывода, клавиатурами, и взаимодействие через графический интерфейс пользователя. (Раздел 10.1)
- Для поддержки потоков ввода, в стандартной библиотеке C++ существует тип istream, а для потоков вывода — ostream. (Раздел 10.2)
- Большую часть времени, мы предполагаем что эти "байты на диске" на самом деле — символы нашего обычного алфавита. Это не всегда так, но это предположение может привести нас очень и очень далеко. (Раздел 10.3)
- Чтобы прочитать файл, нам нужно знать его имя, затем открыть его (для чтения), прочитать все символы и затем закрыть его. (Раздел 10.3)
- Чтобы записать информацию в файл, мы должны определиться с его именем, открыть его (для записи) или создать новый файл, написать в него информацию и закрыть его. (Раздел 10.3)
- Рекомендуется рассчитывать на открытие файла при создании ostream или istream и на закрытие этого файла в момент, когда потоки выходят из области видимости. (Раздел 10.4)
- При считывании данных мы должны ожидать ошибки и уметь их обрабатывать. Разнообразие ошибок чтения данных бесконечно! Однако, istream сокращает их все до четырех возможных вариантов, называемых "состоянием потока":
- good() // операция была успешна
- eof() // мы достигли конца считываемых данных ("конца файла")
- fail() // Произошло что-то неожиданное
- bad() // Прозошло что-то неожиданное и серьезное (Раздел 10.6)
- Чтобы обеспечить надежное чтение, нам нужно уметь решать три проблемы:
- Пользователь печатает значение за пределами диапазона
- Получение пустого значения (конца файла)
- Пользователь печатает что-то неправильного типа (Раздел 10.7)
Глава 11. Настройка ввода/вывода
Большая часть "настройки" это утомительные детали. Поощряйте подход "давайте посмотрим что мы вообще можем сделать, чтобы знать что искать в книгах/мануалах/онлайн_документации, когда нам это понадобится". Я настаиваю на том, что большая часть сложностей досталась нам в наследство от соглашений, принятых до распространения компьютеров, напр. использование ( ) для обозначения отрицательных чисел/убытков/дебита и использование запятой вместо десятичной точки (мы не будем показывать как, ищите сами, если надо и вы живете, скажем, в Германии). Этот курс/книга не рассказывает о региональных стандартах.
Проявите инициативу и отговорите обрабатывать исходные данные по принципу "прочитаем все сразу, а потом будем разбираться что там находится". Для простых данных это будет излишняя нагрузка на студента. В главе 23, если вы дойдете настолько далеко, можно найти более полный список рецептов обработки текста, включая регулярные выражения. Не стесняйтесь указывать на это: "Если вам придется обрабатывть более сложные текстовые данные, сначала прочитайте Главу 23; это может сохранить вам много времени. На данном этапе, нам не нужны сложные инструменты".
Глава 11. Послесловие
- В этой главе мы концентрируемся на том, как адаптировать библиотеку универсальных потоков ввода-вывода iostreams, описанную в главе 10, для особенных нужд и предпочтений. Там представлено множество способов, которыми мы можем оформить ввод и вывод данных для наших задач. (Раздел 11.1)
- Наши программы существуют чтобы помогать людям, а у людей есть сильные предпочтения. Поэтому, как разработчики, мы должны приложить усилия для сохранения баланса между сложностью программы и удовлетворением пользовательских предпочтений. (Раздел 11.1)
- Потоки вывода, ostream, поддерживают множество способов оформления вывода для встроенных типов. Для настройки вывода пользовательских типов, разработчику нужно описать подходящую реализацию оператора <<.
- Выражения << hex и << oct сами по себе не выводят данные. Вместо этого, << hex сообщает потоку, что следующее за ним число нужно отобразить как шестнадцатеричное, а << oct — как восьмеричное.
- Десятичные числа пишутся без префикса, восьмеричные — с префиксом 0, а шестнадцатеричные с префиксом 0x (или 0X). Эта нотация применима для целочисленных литералов в исходном коде C++.
- Для целых чисел применимы манипуляторы: oct, hex, dec, showbase, noshowbase и setw.
- Для форматирования чисел с плавающей точкой можно применить манипуляторы по аналогии с целыми числами.
- К числам с плавающей точкой применимы манипуляторы: fixed, scientific, general (не standard), setprecision и setw.
- Формат general выбирает между форматами scientific и fixed для отображения наиблее точного представления числа с плавающей точкой. По-умолчанию точность составляет 6 цифр.
- Используйте формат по умолчанию (general с точностью до 6 цифр) везде, где нет особенных причин по оформлению. Достаточная причина это "потому что нам нужна большая точность результатов".
- Количество знаков в целых числах ограничивается только если вы явно указали ширину поля для них.
- Свойства потока определяют список и смысл операций, которые мы можем совершать над файлом. Простейший пример: если файл открыли с помощью istream, то мы сможем читать, а если ostream — то писать.
- Точный результат открытия файла может зависеть от операционной системы. Если она не открыть файл определенным способом, то в результате получится поток в состоянии, отличном от good().
- Бинарный ввод/вывод может быть беспорядочным, немного запутанным и склонным к ошибкам, но иногда нам приходится использовать бинарный ввода/вывод просто потому, что некто выбрал его для файлов, которые нам нужно читать или писать.
- Типичный пример это картинка или файл со звуком, для которых нет осмысленного представления в виде символов: фотография или музыкальная запись это просто контейнер с битами.
- В библиотеке iostream реализован переносимый, человекопонятный символьный ввод/вывод, который поддерживается системой типов. Используйте его, когда у вас есть выбор, а в бинарный ввод/вывод ввязывайтесь только когда вам действительно нужно.
- Поток istream, который читает строковую переменную, называется istringstream, а поток, который пишет в строковую переменную — ostringstream.
Глава 12. Графика
Эта глава представляет собой одну долгую демонстрацию. Мы не пытаемся научить студентов чему-то сложному. Мы просто хотим показать немного картинок и намекнуть что это очень просто. После потоков ввода/вывода, слушатели нуждаются в перерыве и небольшом поощрении. Что бы мы ни говорили, многие студенты считают графику настоящим (интересным) применением программирования, а ввод/вывод чисел и текста — "скучным и бесполезным".
В этой лекции, Я быстро переключаюсь между кодом и результатом (скриншотами) и успеваю закончить лекцию, несмотря на количество слайдов.
Обычно я прошу желающего объяснить последний фрагмент кода (содержащий невиданные до сих пор инструменты) чтобы доказать: как только вы поняли общую идею хорошей библиотеки (графической, в данном случае), вы можете читать код без подготовки. С небольшой помощью от лектора, этот "эксперимент" всегда успешен.
Пожалуйста, оставьте дискуссию о значимых деталях реализации и "продвинутых штуках" на следующие две главы. Пожалуйста, сопротивляйтесь желанию покрасоваться, но можете немного растечься мыслью по древу в рассказах о графических приложениях и о том, что они состоят из "подобного кода".
Использование FLTK (или эквивалента) это необходимость. Я давно хочу переделать библиотеку для графического интерфейса с помощью двух других библиотек графики/GUI, чтобы дать великолепный пример переносимости, но никак не получается выделить время. Я не хочу, чтобы студенты думали, что FLTK это какая то особенная библиотека. Я подчеркиваю, что код переносим. У нас всегда есть студенты с машинами под Windows, Linux и Mac, поэтому это хорошая демонстрация переносимости. "Окно на вашей машине выглядит однообразно с другими окнами; код просто запрашивает окно, которое можно создать на вашей машине".
Некоторым студентам нужно очень помочь настроить проект MSVS с GUI. Кроме них, нужно помочь скачать и установить FLTK студентам, которые пожелают запустить код на собственных машинах. Это не было проблемой для наших ассистентов, но с большим количеством студентов (у нас было 180 в классе), вам нужно решить логистические проблемы заранее — не ждите последней ночи.
Да, эта "кнопка Next" это грязный хак, чтобы сделать программирование графического интерфейса похожим на "обычное программирование". Смысл в том, чтобы студенты освоились с графикой, а потом уже схватились за эвенты, виджеты и инверсию управления — обо всем этом будет в Главе 16.
Я начинаю с довольно сложных примеров (координат и кривых) потому что курс поначалу велся у первокурсников электротехники, некоторые из которых считают обычные фигуры легкомысленными. Иногда полезно напоминать, что программирование используется для высоких, серьезных и выгодных и т.п. целей, а не просто для "развлечений и игр".
Если хотите, попробуйте запустить примеры кода из Глав 12-16 "вживую". Кнопка "Next" заменяет "анимацию" для демонстрации. Лично я считаю, что это отвлекает и предпочитаю слайды, но у вас может получиться лучше, чем у меня.
Заметьте, что мы почти приблизились к знакомству с указателями (Глава 17), поэтому в некоторых местах мы "ходим по тонкому льду" объясняя наследование. Непринужденно "ходите дальше" — это вполне возможно.
Глава 13. Графические классы
Тут мы начнем копаться в реализации классов для графического интерфейса и посмотрим немного (очень мало) примеров, используемых в FLTK. Большая часть главы посвящена обзору примеров использования графики; для иллюстрации мы просто углубляемся на один уровень абстракции. Только в следующей части (14) мы доберемся до сердца этой библиотеки: реализации класса shape и объяснении основ объектно-ориентированнных техник, на которые он опирается. Пожалуйста, избегайте этих деталей сейчас: просто покажите что может быть сделано; это мотивирует студентов "осмыслить" техники и концепции. Классы Lines и Text нужны для группировки фигур.
Пример с матрицей цветов знакомит с оператором new и неименованными объектами. Класс Vector_ref можно найти в приложении Д, но, пожалуйста, подавляйте желание познакомить с указателями и "объяснить new". Если кто-нибудь спросит (что очень вероятно), просто ответьте "да, new и Vector_ref используют указатели; мы еще поговорим о них в Главе 17. Пока что мы применяем их, чтобы не именовать каждый создаваемый объект".
Объяснение принципа сокрытия данных и причины указания модификатора private у полей будет темой следующей главы (14).
В разговоре мы затронем только половину примеров из книги, но при этом все необходимые концепции. Книга дает больше примеров для более близкого знакомства с идеями, больше практики чтения кода, и больше полезных классов для упражнений и проектов.
Глава 14. Проектирование графических классов
В этой главе раскрываются две ключевые темы: что отличает набор инструментов от библиотеки, и как выстраивать иерархию классов. Очевидно, эти темы очень связаны. Эта глава полностью посвящена идеям, весьма фундаментальным и ярким идеям, но мы к ним подходим с помощью примеров. Если вы — до последнего технического слайда — будете гнуть линию "мы выбираем хороший способ оформить коллекцию фигур с помощью весьма распространенных техник", то вы глубоко ошибетесь. Вы также можете уйти не туда, если будете тратить слишком много времени объясняя "причудливые словечки, вроде полиморфизма" в попытках исчерпывающе приблизиться теоретической стороне темы. В лучшем случае, я оставляю ремарку "у каждой полезной и популярной идеи есть множетсво имен; "полиморфизм" с греческого значит "множество форм". Логично, что так называется техника, позволяюшая нам определять и использовать графические формы — а также много, много других примеров; поищите сами, если интересно".
Глава 15. Построение графиков функций
Эта глава немного запутанная и заполнена деталями. Ее цель — дать немного практики программирования и примеры после теории из главы 14, но до расширения сознания инверсией управления в главе 15. Мы посмотрим на цепочку примеров, чтобы укрепить понимание прошлой главы и уверить некоторых студентов, что изучаемый материал "реален" (для многих инженеров это значит "числа и математические функции", для многих не-инженеров значит — "графический", и для многих это связано с данными).
Скорость рассказа лекции можно значительно отличаться в зависимости от количества деталей (повторения), которые вы хотите раскрыть. Если студенты усвоили все, что вы рассказывали до сих пор, вы можете закончить быстро (даже с использованием "дополнительных" слайдов с реализацией осей координат, приготовленных на этот случай). Более вероятно, что вы можете застрять тут навсегда. Обязательно продемонстрируйте симуляцию exp(). Мы предпочитаем периодически запускать код вживую. Укажите, что красивый и правильный код может вести себя неожиданно из за особенностей хранения чисел в памяти. Это важно повторять.
Использование глобальной переменной для управления количеством членов в функции expN() это грязный трюк. Как вариант, мы можем создать и увеличивать локальную статическую переменную (еще один грязный трюк). Я не знаю хорошего решения, которое проще чем использование объекта для функции с возможностью инкремента или указания числа членов — и я думаю, что объекты для функций слишком продвинутый или отвлекающий материал на данном этапе. Нельзя сказать, что Function это идеал дизайна. Этот класс просто необходим чтобы добиться отображения, но с ним можно завязнуть при реализации масштабирования и расположения на плоскости. Класс Axis реализован лучше. Если вы хотите, укажите что далеко не весь код можно написать идеально с первого раза. Очень много кода в реальном мире можно написать лучше. На то существует множество причин, и "у нас кончилось время" не последняя из них. Воспринимайте это как проблему пересечения практик управления и программирования, а не как оправдание неряшливой работы.
Техника "симуляции" дает базовые инструменты для проекта, в котором на экране двигаются элементы (например, такие проекты дополнительно требуют механизма задержки (например sleep(); смотрите подробности сами, в главе 26.6.1) и генератора случайных чисел (см 24.7)
Глава 16. Графический интерфейс пользователя
Иногда я иллюстрирую лекцию фотографиями по теме. Разговоры о GUI просто кричат о необходимости примеров. Я использовал iPod и дисплей в кабине летчика. Но примеры работают только если инструктор знаком с ними и заинтересован в них, а мода меняется весьма часто, поэтому я оставил опубликованные слайды "простыми и скучными".
Заметьте, что сейчас в лекции указана примерно половина от примеров из книги. Предполагается, что студенты смогут прочитать главу самостоятельно. Зная студентов, можно сказать что это не лучшее предположение, особенно если у вас нет рычагов управления: ассистентов преподавателя, лабораторных работ, заданий на дом и т.п.
Глава 17: Векторы и динамическая память.
Теперь "каникулы с графикой" закончились. Мы возвращаемся к структурам данных и алгоритмам.
В трех "главах про векторы":
- вводятся понятия массив, указатель и идея освобождения памяти
- демонстрируется реализация std::vector
- происходит знакомство с описанием шаблонов (templates)
- углубляется идея обработки исключений
Как правило, мы предпочитаем делать одну вещь за раз и никогда не делать чего то "абстрактного". Все привязано к конкретным примерам. Это не значит, что основные концепции, правила и техники остались за бортом, просто мы пытаемся сосредоточиться на принципе "сначала конкретное, затем абстрактное".
В основном, мы наслаиваем абстрации над железом пока не дойдем до std:vector (в конце Главы 19). Обязательно подчеркните (и повторите в следующих частях) идеи что:
- "близко к железу" программировать весьма некомфортно и не продуктивно.
- Техники и инструменты языка для построения вектора на низкоуровневых примитивах универсальны и широко используются.
Отметьте, что утечки памяти можно встретить практически во всех языках, даже со сборкой мусора в Java и C# (просто передайте указатель в хеш-таблицу и забудьте об этом — сборщик будет считать, что оно живет вечно).
Когда будете предупреждать о неудобстве и склонности к ошибкам при работе с железом, есть риск демонизировать подобную работу. Большая часть полезной работы требует знания этих техник и возможностей языка — это часть естественного моста между высокоуровневым кодом и железом; оно должно существовать в каждой системе. Если не в самом языке, то в каком-нибудь другом языке (обычно C или C++). Это необходимая и общая база системного программирования. Мы углубимся в детали и повторим это в Главе 18 и Главе 25, где сфокусируемся на программировании встраиваемых систем.
Отметьте, что техника выделения ресурсов в конструкторе и освобождения их в деструкторе — ключевая в большинстве современных техник C++ (и ключ к безопасности исключений; см. Главу 19). Не проходите мимо деструкторов.
Глава 18. Векторы и массивы
Если честно, я считаю большую часть этой главы утомительной, но необходимой. Без нее студенты не поймут как обращаться с указателями и массивами и будут беспомощны как младенцы при работе с кодом в стиле C. Если они не сделают упражнения с указателями и массивами, они забудут все это за несколько недель.
Очень важно объяснить копирующий конструктор и присваивание копированием.
Глава 19. Векторы, шаблоны и исключения
Примеры с reserve(), resize() и push_back() не просто показывают связь памяти с вектором (которую запросто можно считать "магической"). Это также выводит из тени общую концепцию коллекций в STL (Глава 20).
Ключевые идеи, стоящие в основе эффективной обработки исключений: гарантии из стандарта и RAII в этой лекции освещены очень кратко. Это не время углубляться в детали. В книге есть немного больше информации. Однако, роль деструктора при этом нельзя упускать (что может легко случиться, если у вас есть опыт Java).
Очень неловко признавать, что нас "обманывают" вектором с контролем границ в то время, когда стандарт этого не гарантирует (и использует подленький макрос для этого). В последнее время, это стало особенно интересно. Некоторые реализации поставляют библиотеки, которые проверяют по-умолчанию (напр. последний Microsoft C++). У нас обычно находятся студенты с разными реализациями библиотеки, которые даже не подозревают этого. Можно кратко обсудить совместимость, инженерные принципы, компромиссы и (слишком часто) производительность. Это отражено в книге и на одном из слайдов.
Глава 20. Контейнеры и итераторы
Донесение модели итераторов в STL очень важно. Не торопитесь, не пытайтесь умничать и не пытайтесь дать "более реалистичные" или "более продвинутые" примеры раньше времени. STL алгоритм универсален как к типу контейнера, так и к типу элемента (на самом деле, нам совсем не нужен контейнер. Может подойти любой поток элементов). "Осознание" этого на многих студентов действует ошеломляюще, пробуждает интерес и желание исследовать.
Если бы у меня было время, я бы потратил три лекции на эту главу. Мы начинаем с мотивирующего "подтягивающего" примера, за которым следует основа модели STL — с упором на ее возможности обобщения, как причины использования шаблонов и STL. Затем следует повторная демонстрация простого алгоритма, иллюстрирующего использование итераторов (accumulate() и потом find()). Это выглядит достаточно эффективно и необходимо.
Небольшое напоминание: если студенты не будут использовать алгоритмы и контейнеры STL, то они не поймут идеи. Я слышал студентов, которые спустя год после лекций заявляли, что они:
- присутствовали на каждой лекции
- не спали
- ни разу не слышали об "итераторах"
Я слышал подобные истории и об "указателях". Невыполнение задач это рецепт гарантированного бедствия. Выполнение не более одного или двух упражнений в каждой главе это рецепт очень вероятного бедствия. Сохранение знаний это проблема, а написание кода — лучшее ее решение.
Глава 21. Алгоритмы и ассоциативные массивы
Это классическая презентация с разбором кода. Некоторые студенты ненавидят ее: "скучно!". Может быть, но для некоторых эта лекция должна быть интересна (для некоторых так и есть: "ты сможешь!" — да, "это не тормозит?" — нет). Цель — зажечь в умах свет идей обобщения, параметризации, гибкости и переиспользования. Однако если студент не смотрит примеры, то в голове будет темнота — они просто не усвоят принципы при словесном повторении. Даже выполнение задач и упражнений помогает не всем (скорее всего потому что они не делали их, или делали с чьей то помощью без реального понимания).
Глава 22. Идеалы и история
Примите мои извинения за то, что я не упомянул множество интересных языков и практик программирования, но мы ограничены временем и терпением студентов. Мы обычно показываем эту главу прямо перед финальным экзаменом. Многие студенты воспринимают ее как отвлечение от изучения того "что будет на экзамене". Если вы хотите привлечь внимание таких студентов, убедите их что на экзамене будут вопросы по этой главе.
Невозможно охватить весь материал главы в одной лекции на один час. Мы и не пытаемся. Мы выбираем только лучшее. Конечно же, у меня тут есть нечестное преимущество за счет того, что я знаю многих этих людей лично.
Красная стрелка на фотографии Мюррей-Хилл обозначает коридор на 5 этаже, где люди работали в начале 80.
Языки и люди были выбраны для демонстрации C++ в историческом контексте и постепенного увеличения способностей языков к моделированию общих идей. Если бы у меня было больше времени, я бы подчеркнул идею о том, что не существует единого лучшего языка для всех и для всего. Это одна из причин по которым направления Fortran/Cobol/Lisp имеют современные эквиваленты, напр. C++/Java/PHP. Когда я веду лекцию, то обращаю внимание на множество связей между личностями и весьма малое количество организаций-участников.
Идеалы не существуют в вакууме. Идеал становятся настоящим, только когда личность следует за ним.
Глава 23. Работа с текстом
Это первая глава, которая фокусируется на прикладной области, а не на возможностях и техниках языка. Отнеситесь к ней серьезно. Обратите внимание как изученные возможности и техники складываются вместе для решения проблем реального мира. В частности строки, потоки ввода-вывода, ассоциативные массивы.
Обработка текста это один из многих способов применения, но он важен потому что строки это универсальный посредник в коммуникациях человек-человек, человек-машина, машина-человек, машина-машина.
Только в этом месте некоторые студенты видят применение ассоциативных массивов.
Я считаю фотографии тут важны. Многим студентам становится понятнее, если указать, что первые проекты по анализу генома реализовывались как манипуляции со строками и поиск в них (строки состояли из букв A,C,G,T: аденин, цитозин, гуанин, тимин).
Обратите внимание на универсальность регулярных выражений: "Проверка строк по шаблону есть не только в C++". Поддержка проверки по шаблону присутствует во всех языках: C, C#, Javascript, Java, C++, Ruby, Python и т.п.
Текст на первом слайде написан рунами и содержит законы Сконе (раньше была частью Дании, а сейчас в составе Швеции) и никак не связана с содержанием лекции. Это просто пример текста. Точно так же, таблица на странице с обзором это просто пример таблицы (в лекции мы дойдем до таблиц), а контейнеровоз (крупнейший в мире) в приложении это иллюстрация некоторых областей применения.
Глава 24. Числа
Попробуйте использовать библиотеку Matrix, но не углубляйтесь в детали реализации. Большинство библиотек испольются как черные ящики (реализация невидима) и реализация использует комбинацию объектно-ориентированных и шаблонных (generic) техник с добавлением оптимизаций. Все это делает их трудночитаемыми (по крайней мере для новичков).
Если вы не любите математику, это часть не интересная. Если любите — то она выглядит занятной головоломкой. Ключевой момент в том, что вы с помощью языка формируете концепции прикладной области (матрицы в линейной алгебре), а затем передаете их библиотеке для моделирования. Не стоит двигаться вперед используя встроенные инструменты языка непосредственно.
Глава 25. Программирование встроенных систем
Более половины всех компьютеров встроены в другие продукты, и довольно много вакансий сосредоточено в индустрии встраиваемых систем. При том, что встраиваемые системы разделяют взгляды на разработку и ПО — кажется что приложения для ПК господствуют безраздельно. Однако такое представление ложно и вредно.
Я использую возможность, которую предлагают встраиваемые системы, чтобы повторить инструменты языка и техники низкоуровневого программирования. Я также вновь организую обсуждение проблемы трансляции кода для аппаратного обеспечения. По моему опыту с прошлыми и позапрошлыми студентами, они слишком часто теряют (если вообще имели) адекватное представление о трансляции кода высокого уровня на железо. Поэтому, эта глава повторяет темы из Глав 17-19 и (с другой точки зрения) главу 24.
Есть тенденция преуменьшать важность низкоуровневых инструментов и наследие от C. Иногда это даже очерняется — пожалуйста, не делайте этого. Трансляция высокоуровневых конструкций на железо — важная тема. По этому поводу я слышу множество жалоб от индустрии о недостаточной подготовке студентов (в основном студентам не хватает опыта в C или C++). Студенты не должны относиться к трансляции как к магии; и точно так же они не должны застревать на уровне железа в страхе перед слоями абстракций (маскируя это как "заботой об эффективности" или "избегании сложности").
Я выбрал алгоритм TEA отчасти потому, что некоторые студенты считают криптографию захватывающей, отчасти потому что там используется совершенно новый (для студентов) стиль кода и отчасти потому что это непосредственное манипулирование битами в словах (что часто встречается в шифровании, архивировании и графике).
На фотографии на странице 3 запечатлен анализатор крови, используемый в скорой помощи. Самолет на странице 4 это F-16 Объединенных Арабских Эмиратов, а двигатель — одна из первых моделей корабельного дизельного двигателя, разработанная MAN. Это описано в части 1. Студенты инженерных специальностей хорошо воспринимают неотъемлемый идеализм первых слайдов: Мы строим мир, (все) люди рассчитывают на этот тип программ, мы должны сделать его надежным — извинения не принимаются.
Очевидно, что первая часть лекции/главы немного "поучительная", но зато во второй половине мы говорим о пустяках.
Глава 26. Тестирование
Несмотря на отсутствие реальной теории, это может быть наиболее "теоретическая" глава книги. Студенты не знают крупных систем и у нас нет времени на знакомство с фреймворками тестирования, багтрекерами, инструментами статического анализа, мутационного тестирования и т.п. Все, что мы можем сделать — пройтись по основным темам с несколькими искуственными примерами. Однако, я считаю тестирование очень важной темой. Ее почти все игнорируют, но я не могу пройти мимо. На занятиях вполне возможно рассмотреть противопоставление идеи отладки на лету и идеи систематичного поиска ошибок. Проектирование кода с целью упрощения систематичного тестирования — другая сторона той же монеты. Я надеюсь повысить качество проектирования студентов и подготовить их к более тщательному и реалистичному подходу к тестированию.
Глава 27. Язык программирования C
Я никогда не видел программу, которая была бы написана на C лучше, чем на C++. Исключение — отсутствие хорошего компилятора C++ под некоторую систему. Я делал это — и подобные — заявления очень часто за последние десятилетия и никогда не встречал серьезных контраргументов. Я не учитываю эмоциональное пустословие и оскорбления без технической информации как аргументы. Причины для выбора C вместо C++ на самом деле есть (напр. отсутствие компилятора или нехватка специалистов), но среди них нет "производительности" или "широких возможностей языка". В то же время я не считаю, что "сложность" это существенная причина — современные системы намного сложнее, чем C или C++ и эта сложность должна быть где-то отражена — если не в языке (с формально описанной семантикой), то в коде.
Тем не менее, C это полезный язык, у которого много общего с C++. Эволюция этих языков переплеталась с момента создания C++. Мой вклад в C включает:
- Прототипы функций
- Комментарии //
- bool
- const
- Инициализацию в конструкции for (C99)
- inline (C99)
К сожалению, большинство возможностей, перенесенных из C++ в C, были включены в несовместимой форме.
Эта глава/лекция описывает C с точки зрения разработчика на C++. Поэтому она рассказывает чего не хватает и как можно писать код без отсутствующих возможностей. Кроме того, даются примеры "разговорного" C.
Пример с подсчетом слов иллюстрирует разницу между программированием на высоком и низком уровнях. Я использовал его потому что будет неправильно, если весь код презентации будет написан мной. Я считаю что strcmp() и пример интрузивного контейнера нагляднее других показывают применение C (и оба применимы к C++).
barsuksergey
Спасибо. На подоконнике лежит этот монстр, размерами отбивающий напрочь желание даже пролистать. А оказывается, что всё для простых людей.
AlePil
Не так давно из любопытства начал читать этого монстра.
Написано очень приятным языком, читается легко и приятно.
Действительно все для простых людей.