Начало:
1.
Учебник-сайт «Learncpp.com», прочел главу 0 (вводная)2.
Учебник-сайт «LearnCpp.com»: прочел главу 1 (основы) Глава 2 «
C++ Basics: Functions and Files» состоит из 14 подглав (условно их можно назвать «уроками»): с 2.1 по 2.13 и резюмирующей «2.x». Если к первым двум главам (0 и 1) я написал 33 маленьких файла-программы с примерами и упражнениями из учебника, то ко второй главе их получилось 124 штуки (отдельно, не включая примеры к первым двум главам). Готовые файлы с примерами и упражнениями я размещаю в
репозитории на «Гитхабе».
Файлы с программами пришлось дополнительно дробить на подпапки по подглавам (урокам), так как общий список выходил слишком длинный и неудобный в использовании. Я нумерую файлы и с нумерацией вышла накладка. Я планировал нумерацию из расчета, что к одной главе будет не более 100 файлов с примерами и упражнениями, но оказалось, что может быть и больше. Таким образом, чтобы расширить нумерацию файлов до трехзначного числа, придется переименовать все уже загруженные файлы, так как я добавляю ведущие нули в нумерации. Этих файлов, как я уже упоминал, сейчас более сотни, и я пока не знаю, как их переименовать сразу все автоматически. Я знаю, как из веб-интерфейса «Гитхаба» переименовывать файлы по одному, но буду искать способ переименовать группой, так как такой инструмент может пригодиться и в будущем (см. мой пост «
GitHub: переименование множества файлов в репозитории»).
В главе 1 для меня новостью было то, что за последние годы в стандарт языка C++ понадобавляли кучу способов инициализации переменных. А в этой главе наиболее интересным было то, что автор учебника-сайта «LearnCpp.com» решил поставить приемы работы с многофайловыми программами в начало своего учебника, а не в конец, как это сделал Лафоре. (В
учебнике Роберта Лафоре от 2001 года, четвертая редакция, изучение многофайловых программ вынесено в главу 13, то есть ближе к концу учебника.)
Такое решение мне понравилось, ведь, в принципе, объектно-ориентированное программирование (ООП), характерное для языка программирования C++, и подразумевает написание разных классов в отдельных файлах. Так что это логично, что работа с многофайловыми программами должна быть изучена до начала изучения ООП. Думаю, Лафоре тоже до этого дошел бы, если бы продолжал работать над своим учебником.
* * *
В подглаве 2.1 «Introduction to functions» учебника дается введение в понятие «функция». Рассказано о том, что функции бывают определенные программистом-пользователем и определенные в различных библиотеках, в том числе в стандартной библиотеке языка C++. Сказано о том, что функция вызывается из другой функции, которую называют «вызывающей». Определение функции состоит из заголовка функции и тела функции. Функцию можно вызывать множество раз. Функция может вызвать функцию, которая может, в свою очередь, вызвать еще одну функцию и так далее. Определение функции внутри другой функции запрещено (у меня про это есть
отдельный пост).
В подглаве 2.2 «Function return values (value-returning functions)» мы узнаём, что функции бывают с обязательным возвращением значения и бывают с обязательным невозвращением значения (тип void). При определении функции с обязательным возвращением значения в заголовке функции указывается тип возвращаемого значения. При этом возвращение значения из вызываемой функции в вызывающую выполняется с помощью возвращающего утверждения (с использованием ключевого слова return). Возвращаемое значение копируется из вызываемой функции в вызвающую, это называют «возвратом по значению». Описано, как и для чего возвращается значение из функции main. Какие при возвращении значения из функции могут возникать ошибки. Многократное использование функции. Следование принципу «
DRY» («Don’t Repeat Yourself», по-русски «не повторяйтесь»).
В подглаве 2.3 «Void functions (non-value returning functions)» речь идет о функциях, которые не должны возвращать какое-либо значение (тип возвращаемого значения void). Такие функции не могут использоваться в местах, где требуется какое-либо значение (в выражениях) в отличие от функций, которые должны возвращать значение. Попытка возвратить значение из функции, которая не должна возвращать значение, приведет к ошибке компиляции. Ранние возвраты.
В подглаве 2.4 «Introduction to function parameters and arguments» раскрываются понятия «параметр функции» и «аргумент функции». (Кстати, многие их путают и считают синонимами. Ключевая разница в том, что «параметр функции» - это переменная, а «аргумент функции» - это значение.) Использование возвращаемого значения одной функции как аргумента для другой функции.
В подглаве 2.5 «Introduction to local scope» дается введение в понятие «область видимости» (scope). Что такое «локальная переменная». Время жизни (lifetime) и область видимости. Понятие «время жизни переменной» имеет смысл во время работы программы (runtime). Понятие «область видимости» для переменной имеет смысл во время написания и компиляции программы. То, что внутри функции имеется своя, отдельная, область видимости, позволяет создавать в разных функциях переменные с одинаковыми названиями. Сейчас считается, что локальную переменную имеет смысл определять как можно ближе к месту ее первого использования (альтернатива этому способу - определение нужных локальных переменных в начале соответствующей функции; сейчас этот способ считается устаревшим).
В подглаве 2.6 «Why functions are useful, and how to use them effectively» рассказано, зачем в программировании нужны функции и как их использовать эффективно. Это единственная подглава, в которой нет примеров с кодом.
В подглаве 2.7 «Forward declarations and definitions» объясняется разница между понятиями «объявление идентификатора» и «определение идентификатора». Не все понимают разницу, а она важна. В языке программирования C++ всякое определение (definition) одновременно является и объявлением (declaration), но объявления могут даваться отдельно от определений. Кстати, суть этих понятий для разных сущностей языка (переменные, функции, типы и так далее) может отличаться. В языке C++ важное место отведено предварительным объявлениям функций, они сообщают компилятору, что указанная в объявлении функция где-то определена и ее использование в программе не является ошибкой (если определения объявленной функции таки нигде на самом деле нет, то эта ошибка будет обнаружена компоновщиком (linker)). Правило одного определения (
ODR, «one definition rule»).
В подглаве 2.8 «Programs with multiple code files» рассказано про многофайловые программы, то есть программы, код которых размещен в нескольких (во множестве) файлов. Объяснено, для чего это нужно и как компилировать такие программы. Как работать с такими программами в интегрированных средах разработки (IDE) или в командной строке (про последнее у меня есть два отдельных поста:
1 и
2).
В подглаве 2.9 «Naming collisions and an introduction to namespaces» описано, что такое «коллизия» (конфликт) имен и для чего нужны пространства имен (namespace). Что такое «глобальное пространство имен» (глобальная область видимости). Пространство имен std и его квалификатор std::. Здесь обозначение :: - это знак операции (оператор) указания (разрешения) области видимости (по-английски «scope resolution operator»). Директива using namespace std; и почему ее рекомендуют не использовать (кстати, следует знать, что хоть это и директива, но это не директива препроцессора).
В подглаве 2.10 «Introduction to the preprocessor» объясняется, что такое «препроцессор», его место в процессе компиляции программы и зачем он нужен. В наше время препроцессор обычно включен в компилятор, а раньше он чаще был отдельной программой. Препроцессор обрабатывает файлы программы на первом этапе компиляции. Что такое «единица трансляции». Директивы препроцессора. Директива препроцессора #include. Что такое «макроопределение» или «макрос» (macro). Директива препроцессора #define. Макросы, определяющие объект, с заменой текста. Макросы, определяющие объект, без замены текста. Директивы препроцессора для условной компиляции: #ifdef, #ifndef и #endif. Директивы препроцессора #if 0 и #if 1, которые можно использовать для закомментирования/раскомментирования кода в отладочных целях. Область видимости директив препроцессора.
В подглаве 2.11 «Header files» рассказано о заголовочных файлах (еще их называют просто «заголовками»). Для чего их следует использовать и для чего их не следует использовать (неправильное использование этих файлов может вызвать ошибки при компиляции программы). Заголовочные файлы стандартной библиотеки языка C++ и их включение в свою программу. Написание своих заголовочных файлов для своей программы. Включение своих заголовочных файлов в основные файлы с кодом. Использование при включении файлов угловых скобок и двойных кавычек. Какое расширение выбрать для заголовочного файла. Почему часть заголовочных файлов стандартной библиотеки языка C++ имеет расширение, а другая часть - не имеет (и какие из этих заголовочных файлов следует использовать в своих программах). Включение заголовочных файлов не из текущей папки (директории). В заголовочные файлы могут быть включены другие заголовочные файлы и так далее. Порядок включения заголовочных файлов в свою программу. Лучшие практики при написании заголовочных файлов.
В подглаве 2.12 «Header guards» раскрывается, что такое «защита (защитник) заголовочного файла от дублирующих включений». Проблема дублирующих включений одного и того же заголовочного файла в файл с исходным кодом. Защита от дублирующих включений с помощью директив препроцессора. Эта защита предотвращает дублирующие включения заголовочного файла в один отдельный файл с исходным кодом, но не предотвращает включение заголовочного файла в разные файлы с исходным кодом одной программы (так и должно быть). Директива препроцессора #pragma once, ее преимущества и недостатки.
В подглаве 2.13 «How to design your first programs» рассказано и показано на примерах, как можно планировать создание своих программ. Без планирования невозможно создавать программы, начиная с тех, которые чуть сложнее самых тривиальных и заканчивая большими и сложными. Определение цели программы (что она будет делать). Определение требований к программе. Требования к программе включают ограничения программы (бюджет на ее создание; сроки создания; пространство, которое она будет занимать в постоянной памяти; количество оперативной памяти, которое она будет потреблять и тому подобное) и функционал программы. Определение инструментов для написания программы; целевой платформы, на которой будет работать программа, и создание архива файлов с исходным кодом программы. Разбиение сложных задач на более мелкие и легкие. Или наоборот: сведение множества отдельных мелких задач в группы. Планирование последовательности событий при работе с программой. Реализация программы: написание плана, наметка прототипов функций, реализация и тестирование каждой функции по отдельности, сведение функций в общую программу и финальное тестирование. Советы по написанию программ.
Наконец, в подглаве «2.x» подводится краткое резюме по всей главе, перечисляются определения основных понятий, пройденных в этой главе. В конце каждой подглавы есть проверочный опрос с вопросами и упражнениями.