С самоучитель. Уроки по с ravesli com 2 предисловие
Скачать 2.56 Mb.
|
Правило: Никогда не используйте вложенные комментарии. Как правильно писать комментарии? Во-первых, на уровне библиотек/программ/функций комментарии отвечают на вопрос "ЧТО?": "Что делают эти библиотеки/программы/функции?". Например: // Эта программа вычисляет оценку студента за семестр на основе его оценок за модули // Эта функция использует метод Ньютона для вычисления корня функции // Следующий код генерирует случайное число Все эти комментарии позволяют понять, что делает программа, без необходимости смотреть на исходный код. Это особенно важно ravesli.com 55 специалистам, работающим в команде, где не каждый специалист будет знаком со всем имеющимся кодом. Во-вторых, внутри библиотек/программ/функций комментарии отвечают на вопрос "КАК?": "Как код выполняет задание?": /* Для расчёта итоговой оценки студента, мы добавляем все его оценки за уроки и домашние задания, а затем делим получившееся число на общее количество оценок. Таким образом мы получаем средний балл студента. */ // Чтобы получить рандомный (случайный) элемент, мы выполняем следующее: // 1) Составляем список всех элементов. // 2) Вычисляем среднее значение для каждого элемента, исходя из его веса, цвета и цены. // 3) Выбираем любое число. // 4) Определяем соответствие элемента случайно выбранному числу. // 5) Возвращаем случайный элемент. Эти комментарии позволяют пользователю понять, каким образом код выполняет поставленное ему задание. В-третьих, на уровне стейтментов (однострочного кода) комментарии отвечают на вопрос "ПОЧЕМУ?": "Почему код выполняет задание именно таким образом, а не другим?". Плохой комментарий на уровне стейтментов объясняет, что делает код. Если вы когда-нибудь писали код, который был настолько сложным, что нужен был комментарий, который бы объяснял, что он делает, то вам нужно было бы не писать комментарий, а переписывать целый код. Примеры плохих и хороших однострочных комментариев: Плохой комментарий: // Присваиваем переменной sight значение 0 sight = 0 ; (По коду это и так понятно) ravesli.com 56 Хороший комментарий: // Игрок выпил зелье слепоты и ничего не видит sight = 0 ; (Теперь мы знаем, ПОЧЕМУ зрение у игрока равно нулю) Плохой комментарий: // Рассчитываем стоимость элементов cost = items / 2 * storePrice ; (Да, мы видим, что здесь подсчёт стоимости, но почему элементы делятся на 2?) Хороший комментарий: // Нам нужно разделить все элементы на 2, потому что они куплены по парам cost = items / 2 * storePrice ; ( Теперь понятно!) Программистам часто приходится принимать трудные решения по поводу того, каким способом решить проблему. А комментарии и существуют для того, чтобы напомнить себе (или объяснить другим) причину, почему вы написали код именно так, а не иначе. Хорошие комментарии: // Мы решили использовать список вместо массива, потому что // массивы осуществляют медленную вставку. // Мы используем метод Ньютона для вычисления корня функции, так как // другого детерминистического способа решения этой задачи нет И, наконец, комментарии нужно писать так, чтобы человек, который не имеет ни малейшего представления о том, что делает ваш код – смог в нём разобраться. Очень часто случаются ситуации, когда программист ravesli.com 57 говорит: «Это же совершенно очевидно, что делает код! Я это точно не забуду". Угадайте, что случится через несколько недель или даже дней? Это не совершенно очевидно, и вы удивитесь, как скоро вы забудете, что делает ваш код. Вы (или кто-то другой) будете очень благодарны себе за то, что оставите комментарии, объясняя на человеческом языке что, как и почему делает ваш код. Читать отдельные строки кода – легко, понимать их логику и смысл – сложно. Подытожим: На уровне библиотек/программ/функций оставляйте комментарии, отвечая на вопрос "ЧТО?". Внутри библиотек/программ/функций оставляйте комментарии, отвечая на вопрос "КАК?". На уровне стейтментов оставляйте комментарии, отвечая на вопрос " ПОЧЕМУ?". Закомментирование Закомментировать код – это конвертировать одну или нескольких строк кода в комментарии. Таким образом, вы сможете (временно) исключить части кода от компиляции. Чтобы закомментировать одну строчку кода, используйте однострочные символы комментирования //. Не закомментировано: std :: cout << 1 ; Закомментировано: // std::cout << 1; ravesli.com 58 Чтобы закомментировать блок кода, используйте однострочные символы комментирования // на каждой строке или символы многострочного комментария /* */. Не закомментировано: std :: cout << 1 ; std :: cout << 2 ; std :: cout << 3 ; Закомментировано символами однострочного комментария: // std::cout << 1; // std::cout << 2; // std::cout << 3; Закомментировано символами многострочного комментария: /* std::cout << 1; std::cout << 2; std::cout << 3; */ Есть несколько причин, зачем использовать закомментирование: Причина №1: Вы работаете над новой частью кода, которая пока что не рабочая, но вам нужно запустить программу. Компилятор не позволит выполнить программу, если в ней будут ошибки. Временное отделение нерабочего кода от рабочего комментированием позволит вам запустить программу. Когда код будет рабочий – сможете его легко раскомментировать и продолжить работу. Причина №2: Вы написали код, который компилируется, но работает не так как нужно и сейчас у вас нет времени с этим разбираться. Закомментируйте код, а затем, когда будет время, исправьте в нём ошибки. ravesli.com 59 Причина №3: Поиск корня ошибки. Если программа производит не те результаты (или вообще происходит сбой), полезно будет поочерёдно "отключать" части вашего кода, чтобы понять какие из них рабочие, а какие создают проблемы. Если вы закомментируете одну или несколько строчек кода и программа начнёт корректно работать (или пропадут сбои), шансы того, что последнее, что вы закомментировали, является ошибкой – очень велики. После этого вы сможете разобраться с тем, почему же этот код не работает так, как нужно. Причина №4: Тестирование нового кода. Вместо удаления старого кода, вы можете его закомментировать и оставить для справки, пока не будете уверены, что ваш новый код работает так, как нужно. Как только вы будете уверены в новом коде, то сможете без проблем удалить старые фрагменты кода. Если же новый код у вас будет работать не так, как нужно – вы сможете его удалить и выполнить откат к старому коду. Примечание: Во всех следующих уроках я буду использовать комментарии в иллюстративных целях. Внимательные читатели смогут заметить, что по стандартам выше большинство из этих комментариев будут плохими. Но помните, что использовать я их буду в образовательных целях, а не для демонстрации хороших примеров. ravesli.com 60 Урок №10. Переменные. Инициализация и Присваивание Программируя на языке C++ мы создаём, обрабатываем и уничтожаем объекты. Объект – это часть памяти, которая может хранить значение. Аналогия: почтовый ящик, где мы храним информацию и откуда её извлекаем. Все компьютеры имеют оперативную память, которую используют программы. При создании объекта часть оперативной памяти выделяется под этот объект. Большинство объектов, с которыми мы будем работать в C++, являются переменными. Переменные Стейтмент a = 8; выглядит довольно простым: мы присваиваем значение 8 переменной a. Но что такое a? a – это переменная, объект с именем. В этом уроке, мы рассмотрим только целочисленные переменные. Целое число – это число, которое можно записать без дроби, например: - 11, - 2, 0, 5 или 34. Для создания переменной используется стейтмент объявления (разницу между объявлением и определением переменной мы рассмотрим несколько позже). Вот пример объявления целочисленной переменной a (которая может содержать только целые числа): int a ; При выполнении этой инструкции центральным процессором часть оперативки выделяется под этот объект. Например, предположим, что переменной a присваивается ячейка памяти под номером 150. Когда программа видит переменную a в выражении или в стейтменте, то она понимает, что для того, чтобы получить значение этой переменной – нужно заглянуть в ячейку памяти под номером 150. ravesli.com 61 Одной из наиболее распространённых операций с переменными является операция присваивания. Например: a = 8 ; Когда процессор выполняет эту инструкцию, он читает её как "поместить значение 8 в ячейку памяти под номером 150". Затем мы сможем вывести это значение на экран с помощью std::cout: std :: cout << a ; // выводим значение переменной a (ячейка памяти под номером 150) на экран l-values и r-values В C++ все переменные являются l-values. l-value (в переводе "л- значение", произносится как "ел-валью") – это значение, которое имеет свой адрес в памяти. Поскольку все переменные имеют адреса, то они все являются l-values (например: переменные a, b, c – все являются l- values). l от слова "left", так как только значения l-values могут находиться в левой стороне в операциях присваивания (в противном случае мы получим ошибку). Например, стейтмент 9 = 10; вызовет ошибку компилятора, так как 9 не является l-value. Число 9 не имеет своего адреса в памяти и, таким образом, мы ничего не можем ему присвоить (9 = 9 и ничего здесь не изменить). Противоположностью l-value является r-value (в переводе "р-значение", произносится как «ер-валью»). r-value – это значение, которое не имеет постоянного адреса в памяти. Примерами могут быть единичные числа (например, 7, которое вычисляется в 7) или выражения (например, 3 + х , которое вычисляется в значение х плюс 3). Вот несколько примеров операций присваивания с использованием r- values: int a ; // объявляем целочисленную переменную a ravesli.com 62 a = 5 ; // 5 вычисляется в 5, которое затем присваивается переменной а a = 4 + 6 ; // 4 + 6 вычисляется в 10, которое затем присваивается переменной а int b ; // объявляем целочисленную переменную b b = a ; // a вычисляется в 10 (с предыдущих операций), которое затем присваивается переменной b b = b ; // b вычисляется в 10, которое затем присваивается переменной b (ничего не происходит) b = b + 2 ; // b + 2 вычисляется в 12, которое затем присваивается переменной b Давайте детальнее рассмотрим последнюю операцию присваивания: b = b + 2 ; Здесь переменная b используется в двух различных контекстах. Слева b используется как l-value (переменная с адресом в памяти), справа b используется как r-value и производит отдельное значение (в данном случае 12). При выполнении этого стейтмента, компилятор видит следующее: b = 10 + 2 ; И здесь уже становится понятным, какое значение присваивается переменной b. Сильно беспокоиться о l-values или r-values сейчас не нужно, так как мы ещё вернемся к этой теме в следующих уроках. Всё, что вам нужно сейчас запомнить – это то, что в левой стороне операции присваивания всегда должно находиться l-value (которое имеет свой адрес в памяти), а в правой стороне операции присваивания – r-value (которое производит какое-то значение). Инициализация vs. Присваивание В C++ есть две похожие концепции, которые новички часто путают: присваивание и инициализация. ravesli.com 63 После объявления переменной, ей можно присвоить значение с помощью оператора присваивания (знак равенства =): int a ; // это объявление переменной a = 8 ; // а это присваивание переменной a значения 8 В C++ вы можете объявить переменную и присвоить ей значение одновременно. Это называется инициализацией (или ещё "определением"). int a = 8 ; // инициализируем переменную a значением 8 Переменная может быть инициализирована только после операции объявления. Хотя эти два понятия близки по своей сути и часто могут использоваться для достижения одних и тех же целей, всё же в некоторых случаях следует использовать инициализацию, вместо присваивания, а в некоторых, наоборот, присваивание вместо инициализации. Правило: Если у вас изначально имеется значение для переменной – используйте инициализацию, вместо присваивания. Неинициализированные переменные В отличие от других языков программирования, C и C++ не инициализируют переменные определёнными значениями (например, нулём) по умолчанию. Поэтому при создании переменной ей присваивается ячейка в памяти, в которой уже может находиться какой- нибудь мусор! Переменная без значения (со стороны программиста или пользователя) называется неинициализированной переменной. Использование неинициализированных переменных может привести к ошибкам. Например: // #include "pch.h" // раскомментируйте, если используете Visual Studio #include ravesli.com 64 int main () { // объявляем целочисленную переменную a int a ; // выводим значение переменной a на экран (a - неинициализированная переменная) std :: cout << a ; return 0 ; } В этом случае компилятор присваивает переменной a ячейку в памяти, которая в данный момент свободна (не используется). Затем значение переменной a отправляется на вывод. Но что мы увидим на экране? Ничего, так как компилятор это не пропустит – выведется ошибка, что переменная a является неинициализированной. В более старых версиях Visual Studio компилятор мог бы вывести какое-то вообще левое значение (например, 7177728, т.е. мусор), которое было бы содержимым той ячейки памяти, которую он присвоил нашей переменной. Использование неинициализированных переменных является одной из самых распространённых ошибок начинающих программистов, но, к счастью, большинство современных компиляторов выдадут ошибку во время компиляции, если обнаружат неинициализированную переменную. Хорошей практикой считается всегда инициализировать свои переменные. Это будет гарантией того, что ваша переменная всегда имеет одно и то же значение и вы не получите ошибку от компилятора. Правило: Убедитесь, что все ваши переменные в программе имеют значения (либо через инициализацию, либо через операцию присваивания). ravesli.com 65 Тест Каков результат выполнения следующих стейтментов? int a = 6 ; a = a - 3 ; std :: cout << a << std :: endl ; // №1 int b = a ; std :: cout << b << std :: endl ; // №2 // в этом случае a + b является r-value std :: cout << a + b << std :: endl ; // №3 std :: cout << a << std :: endl ; // №4 int c ; std :: cout << c << std :: endl ; // №5 ravesli.com 66 Урок №11. cout, cin и endl В этом уроке мы рассмотрим такие объекты, как cout, endl и cin. std::cout Как мы уже говорили в предыдущих уроках, объект std::cout (который находится в библиотеке iostream) используется для вывода данных на экран (в консольное окно). В качестве напоминания, вот наша программа «Hello, world!»: #include () { std :: cout << "Hello, world!" ; return 0 ; } Для вывода нескольких предложений на одной строке оператор вывода << нужно использовать несколько раз. Например: #include () { int a = 7 ; std :: cout << "a is " << a ; return 0 ; } Программа выведет: a is 7 А какой результат выполнения следующей программы? #include () { std :: cout << "Hi!" ; std :: cout << "My name is Anton." ; return 0 ; } ravesli.com 67 Возможно, вы удивитесь, но: Hi!My name is Anton. std::endl Если текст нужно вывести раздельно (на нескольких строках) – используйте std::endl. При использовании с std::cout, std::endl вставляет символ новой строки. Таким образом, мы перемещаемся к началу следующей строки. Например: #include () { std :: cout << "Hi!" << std :: endl ; std :: cout << "My name is Anton." << std :: endl ; return 0 ; } Результат: Hi! My name is Anton. std::cin std::cin является противоположностью std::cout. В то время как std::cout выводит данные в консоль с помощью оператора вывода <<, std::cin получает данные от пользователя с помощью оператора ввода >> . Используя std::cin мы можем получать и обрабатывать пользовательский ввод. #include () { std :: cout << "Enter a number: " ; // просим пользователя ввести любое число int a = 0 ; std :: cin >> a ; // получаем пользовательское число и сохраняем его в переменную a std :: cout << "You entered " << a << std :: endl ; ravesli.com 68 return 0 ; } Попробуйте скомпилировать и запустить эту программу. При запуске вы увидите "Enter a number:", затем программа будет ждать, пока вы введёте число. Как только вы это сделаете и нажмёте Enter, программа выведет "You entered", а затем ваше число. Например, я ввёл 7: Enter a number: 7 You entered 7 Это самый простой способ получения данных от пользователя. Мы будем его использовать в дальнейших примерах. Если ваше окно закрывается сразу после ввода числа, то см. Урок №7 (там есть решение этой проблемы). Если же ввести действительно большое число, то вы получите переполнение, так как переменная а может содержать числа только определённого размера/диапазона. Если число больше/меньше допустимых максимумов/минимумов, то происходит переполнение. Мы ещё детальнее об этом поговорим в следующих уроках. |