3.10 – Поиск проблем до того, как они станут проблемами

Добавлено 24 апреля 2021 в 19:16

Когда вы делаете семантическую ошибку, эта ошибка при запуске вашей программы может быть или не быть сразу заметна. Проблема может скрываться незамеченной в вашем коде в течение долгого времени, прежде чем новый добавленный код или изменившиеся обстоятельства заставят ее проявиться как неисправность программы. Чем дольше ошибка находится в коде до того, как она будет обнаружена, тем больше вероятность, что ее будет труднее найти, и то, что, возможно, было легко исправить изначально, превращается в приключение по отладке, которое съедает время и энергию.

Что мы можем с этим поделать?

Не делайте ошибок

Что ж, лучше всего вообще не делать ошибок. Вот неполный список вещей, которые могут помочь избежать ошибок:

  • следуйте лучшим практикам;
  • не программируйте, когда устали;
  • поймите, где в языке часто встречаются подводные камни (все те вещи, которые мы предупреждаем вас не делать);
  • делайте свои программы простыми;
  • не позволяйте вашим функциям быть слишком длинными;
  • по возможности используйте стандартную библиотеку, а не пишите собственный код;
  • обязательно комментируйте код.

Рефакторинг функций

По мере того, как вы добавляете в свои программы новые возможности («поведенческие изменения»), вы обнаружите, что некоторые из ваших функций становятся длиннее. По мере того, как функции становятся длиннее, они становятся сложнее и труднее для понимания.

Один из способов решить эту проблему – разбить одну длинную функцию на несколько более коротких функций. Этот процесс внесения структурных изменений в ваш код без изменения его поведения (обычно для того, чтобы сделать его более удобным для сопровождения), называется рефакторингом.

Итак, когда функция считается слишком длинной? Функция, занимающая более одного экрана кода по вертикали, обычно считается слишком длинной – если вам нужно прокрутить, чтобы прочитать всю функцию, понятность функции значительно упадет. Чем короче, тем лучше – функции меньше десяти строк хороши. Функции, длина которых меньше пяти строк, даже лучше.

Ключевой момент


При внесении изменений в код, вносите поведенческие изменения ИЛИ структурные изменения, после чего повторите проверку кода на правильность работы. Внесение поведенческих и структурных изменений одновременно, как правило, приводит к большему количеству ошибок, а также к ошибкам, которые труднее найти.

Введение в защитное программирование

Ошибки могут быть не только из-за ваших собственных действий (например, неправильная логика), но и возникать, когда ваши пользователи используют приложение не так, как вы ожидали. Например, если вы просите пользователя ввести целое число, а вместо этого он вводит букву, как ваша программа ведет себя в таком случае? Если вы этого не ожидали и не добавили обработку ошибок для этого случая, вероятно, это не очень хорошо.

Защитное программирование – это практика, при которой программист пытается предвидеть все возможные способы неправильного использования программного обеспечения конечными пользователями или другими разработчиками (включая самого программиста), использующих код. Эти некорректные использования часто можно обнаружить, а затем смягчить (например, попросив пользователя, который ввел неверные входные данные, повторить попытку).

В будущих уроках мы рассмотрим темы, связанные с обработкой ошибок.

Быстрый поиск ошибок

Поскольку в больших программах трудно не допустить ошибок, лучше быстро выявлять ошибки, которые вы делаете.

Лучший способ сделать это – писать код понемногу, а затем тестировать его, чтобы убедиться, что он работает.

Однако есть несколько других методов, которые мы также можем использовать.

Введение в функции тестирования

Один из распространенных способов помочь обнаружить проблемы с вашей программой – написать функции тестирования, чтобы «проверить» написанный вами код. Вот примитивный пример такой функции, просто иллюстрации:

#include <iostream>
 
int add(int x, int y)
{
	return x + y;
}
 
void testadd()
{
	std::cout << "This function should print: 2 0 0 -2\n";
	std::cout << add(1, 1) << ' ';
	std::cout << add(-1, 1) << ' ';
	std::cout << add(1, -1) << ' ';
	std::cout << add(-1, -1) << ' ';
}
 
int main()
{
	testadd();
 
	return 0;
}

Функция testadd() тестирует функцию add(), вызывая ее с разными значениями. Если все значения соответствуют нашим ожиданиям, мы можем быть достаточно уверены в том, что функция работает правильно. Более того, мы можем сохранить эту функцию и запускать ее каждый раз, когда мы изменяем функцию add(), чтобы убедиться, что случайно не сломали ее.

Это примитивный пример модульного тестирования (юнит-тестирования, unit testing), который представляет собой метод тестирования программного обеспечения, с помощью которого тестируются небольшие блоки (модули) исходного кода, чтобы определить их правильность.

Как и в случае с фреймворками логгирования, для модульного тестирования можно использовать множество сторонних фреймворков. Также можно написать свой собственный, хотя нам понадобится большее знание языка, чтобы отразить эту тему. Мы вернемся к этому на следующих уроках.

Введение в ограничения

Методы, основанные на ограничениях, включают в себя добавление некоторого дополнительного кода (который при желании может быть скомпилирован в неотладочной сборке) для проверки того, что некоторый набор предположений или ожиданий не нарушается.

Например, если бы мы писали функцию для вычисления факториала числа, которая ожидает неотрицательный аргумент, эта функция, прежде чем продолжить, могла бы проверить, чтобы вызывающая сторона передала неотрицательное число. Если вызывающая сторона передает отрицательное число, тогда эта функция может немедленно завершиться с ошибкой, а не давать неопределенный результат, что поможет обеспечить немедленное обнаружение проблемы.

Один из распространенных способов сделать это – использовать assert и static_assert, которые мы рассмотрим в уроке «7.17 - assert и static_assert».

Отлов распространенных проблем

Программисты склонны совершать определенные виды типовых ошибок, и некоторые из этих ошибок могут быть обнаружены программами, обученными их искать. Эти программы, обычно известные как инструменты статического анализа (иногда неофициально называемые линтерами, linter), представляют собой программы, которые анализируют ваш код для выявления конкретных семантических проблем (в данном контексте «статический» означает, что эти инструменты анализируют исходный код). Проблемы, обнаруженные инструментами статического анализа, могут быть, а могут и не быть причиной какой-либо конкретной проблемы, которая у вас есть, но могут помочь указать на хрупкие области кода или моменты, которые могут быть проблематичными при определенных обстоятельствах.

В вашем распоряжении уже есть один инструмент статического анализа – ваш компилятор! Помимо обеспечения синтаксической правильности вашей программы, большинство современных компиляторов C++ будут выполнять небольшой статический анализ для выявления некоторых распространенных проблем. Например, многие компиляторы предупредят вас, если вы попытаетесь использовать переменную, которая не была инициализирована. Если вы еще этого не сделали, настройте уровни предупреждений и ошибок компилятора (смотрите урок «0.11 – Настройка компилятора: уровни предупреждений и ошибок»).

Существуют и другие инструменты статического анализа, некоторые из которых могут идентифицировать более 300 типов ошибок программирования. А в больших программах они могут выявить десятки или сотни потенциальных проблем. В наших небольших учебных программах нет необходимости устанавливать или запускать эти инструменты, поскольку предупреждений компилятора во время обучения обычно будет достаточно, но для большой кодовой базы это рекомендуется.

Теги

C++ / CppDebugДля начинающихМодульное тестирование / Юнит-тестирование / Unit testingОбучениеОтладкаПрограммированиеРефакторинг

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.