Современные рекомендации по C++ для исключений и обработки ошибокModern C++ best practices for exceptions and error handling

В современных C++ в большинстве случаев предпочтительным способом сообщить и обрабатывались как логические ошибки, так и ошибки времени выполнения — использовать исключения.In modern C++, in most scenarios, the preferred way to report and handle both logic errors and runtime errors is to use exceptions. Особенно это касается того, что стек может содержать несколько вызовов функций между функцией, которая обнаруживает ошибку, и функцией, которая имеет контекст для ее устранения.It's especially true when the stack might contain several function calls between the function that detects the error, and the function that has the context to handle the error. Исключения предоставляют формальный, четко определенный способ для кода, который обнаруживает ошибки для передачи информации вверх по стеку вызовов.Exceptions provide a formal, well-defined way for code that detects errors to pass the information up the call stack.

Использовать исключения для кода исключительного пользованияUse exceptions for exceptional code

Ошибки программы часто делятся на две категории: логические ошибки, вызванные ошибками программирования, например, ошибкой «индекс вне диапазона».Program errors are often divided into two categories: Logic errors that are caused by programming mistakes, for example, an "index out of range" error. И ошибки времени выполнения, которые выходят за рамки управления программистом, например "ошибка" Сетевая служба недоступна ".And, runtime errors that are beyond the control of programmer, for example, a "network service unavailable" error. В программировании в стиле C и в COM Управление отчетами об ошибках осуществляется либо путем возвращения значения, представляющего код ошибки, либо кода состояния для конкретной функции, либо путем установки глобальной переменной, которую вызывающий может дополнительно получить после каждого вызова функции, чтобы проверить, были ли обнаружены ошибки.In C-style programming and in COM, error reporting is managed either by returning a value that represents an error code or a status code for a particular function, or by setting a global variable that the caller may optionally retrieve after every function call to see whether errors were reported. Например, при программировании COM для передачи ошибок вызывающему объекту используется возвращаемое значение HRESULT.For example, COM programming uses the HRESULT return value to communicate errors to the caller. API-интерфейс Win32 содержит GetLastError функцию для получения последней ошибки, о которой сообщил стек вызовов.And the Win32 API has the GetLastError function to retrieve the last error that was reported by the call stack. В обоих случаях для распознавания кода и реагирования на него требуется вызывающая сторона.In both of these cases, it's up to the caller to recognize the code and respond to it appropriately. Если вызывающий объект не обрабатывает код ошибки явным образом, программа может аварийно завершить работу без предупреждения.If the caller doesn't explicitly handle the error code, the program might crash without warning. Или можно продолжить выполнение с использованием неверных данных и получить неверные результаты.Or, it might continue to execute using bad data and produce incorrect results.

Исключения являются предпочтительными в современных C++ по следующим причинам:Exceptions are preferred in modern C++ for the following reasons:

  • Исключение приводит к тому, что вызывающий код распознает состояние ошибки и обрабатывает его.An exception forces calling code to recognize an error condition and handle it. Необработанные исключения останавливают выполнение программы.Unhandled exceptions stop program execution.

  • Исключение переходит к точке в стеке вызовов, которая может справиться с ошибкой.An exception jumps to the point in the call stack that can handle the error. Промежуточные функции могут позволить распространить исключение.Intermediate functions can let the exception propagate. Они не должны координироваться с другими уровнями.They don't have to coordinate with other layers.

  • Механизм обратной записи исключений уничтожает все объекты в области действия после возникновения исключения в соответствии с четко определенными правилами.The exception stack-unwinding mechanism destroys all objects in scope after an exception is thrown, according to well-defined rules.

  • Исключение позволяет четко отделить код, который определяет ошибку, и код, обрабатывающий ошибку.An exception enables a clean separation between the code that detects the error and the code that handles the error.

В следующем упрощенном примере показан синтаксис, необходимый для генерации и перехвата исключений в C++.The following simplified example shows the necessary syntax for throwing and catching exceptions in C++.

#include <stdexcept>
#include <limits>
#include <iostream>

using namespace std;

void MyFunc(int c)
{
    if (c > numeric_limits< char> ::max())
        throw invalid_argument("MyFunc argument too large.");
    //...
}

int main()
{
    try
    {
        MyFunc(256); //cause an exception to throw
    }

    catch (invalid_argument& e)
    {
        cerr << e.what() << endl;
        return -1;
    }
    //...
    return 0;
}

Исключения в C++ похожи на такие языки, как C# и Java.Exceptions in C++ resemble ones in languages such as C# and Java. В try блоке при возникновении исключения оно будет перехвачено первым связанным catch блоком, тип которого совпадает с типом исключения.In the try block, if an exception is thrown it will be caught by the first associated catch block whose type matches that of the exception. Иными словами, выполнение переходит от throw оператора к catch оператору.In other words, execution jumps from the throw statement to the catch statement. Если подходящий блок catch не найден, std::terminate вызывается метод и программа завершает работу.If no usable catch block is found, std::terminate is invoked and the program exits. В C++ может быть вызван любой тип; Однако рекомендуется создавать тип, прямо или косвенно производный от std::exception .In C++, any type may be thrown; however, we recommend that you throw a type that derives directly or indirectly from std::exception. В предыдущем примере тип исключения invalid_argument определен в стандартной библиотеке в <stdexcept> файле заголовка.In the previous example, the exception type, invalid_argument, is defined in the standard library in the <stdexcept> header file. C++ не предоставляет или не требует finally блока, чтобы гарантировать освобождение всех ресурсов при возникновении исключения.C++ doesn't provide or require a finally block to make sure all resources are released if an exception is thrown. Идиома получения ресурсов — инициализация (RAII), которая использует интеллектуальные указатели, предоставляет необходимые функции для очистки ресурсов.The resource acquisition is initialization (RAII) idiom, which uses smart pointers, provides the required functionality for resource cleanup. Дополнительные сведения см. в разделе руководство. проектирование безопасности исключений.For more information, see How to: Design for exception safety. Дополнительные сведения о механизме развертывания стека C++ см. в разделе исключения и очистка стека.For information about the C++ stack-unwinding mechanism, see Exceptions and stack unwinding.

Основные рекомендацииBasic guidelines

Надежная обработка ошибок является сложной задачей в любом языке программирования.Robust error handling is challenging in any programming language. Хотя исключения предоставляют несколько функций, которые поддерживают хорошую обработку ошибок, они не могут выполнить всю работу.Although exceptions provide several features that support good error handling, they can't do all the work for you. Чтобы реализовать преимущества механизма исключения, помните об исключениях при проектировании кода.To realize the benefits of the exception mechanism, keep exceptions in mind as you design your code.

  • Используйте утверждения, чтобы проверить наличие ошибок, которые не должны возникать.Use asserts to check for errors that should never occur. Используйте исключения для проверки ошибок, которые могут возникать, например, ошибок при проверке входных данных для параметров открытых функций.Use exceptions to check for errors that might occur, for example, errors in input validation on parameters of public functions. Дополнительные сведения см. в разделе исключения и утверждения .For more information, see the Exceptions versus assertions section.

  • Используйте исключения, если код, обрабатывающий ошибку, отделен от кода, который обнаруживает ошибку одним или несколькими промежуточными вызовами функций.Use exceptions when the code that handles the error is separated from the code that detects the error by one or more intervening function calls. Рассмотрите возможность использования кодов ошибок в циклах, критических для производительности, когда код, обрабатывающий ошибку, тесно связан с кодом, который его обнаруживает.Consider whether to use error codes instead in performance-critical loops, when code that handles the error is tightly coupled to the code that detects it.

  • Для каждой функции, которая может выдавать или распространять исключение, следует предоставить одно из трех гарантий исключений: строгая гарантия, Базовая гарантия или "Throw" (Except).For every function that might throw or propagate an exception, provide one of the three exception guarantees: the strong guarantee, the basic guarantee, or the nothrow (noexcept) guarantee. Дополнительные сведения см. в разделе руководство. проектирование безопасности исключений.For more information, see How to: Design for exception safety.

  • Вызывайте исключения по значению, перехватите их по ссылке.Throw exceptions by value, catch them by reference. Не перехватывайте объекты, которые не могут быть обработаны.Don’t catch what you can't handle.

  • Не используйте спецификации исключений, которые являются устаревшими в C++ 11.Don't use exception specifications, which are deprecated in C++11. Дополнительные сведения см. в разделе спецификации исключений и noexcept раздел.For more information, see the Exception specifications and noexcept section.

  • Используйте типы исключений стандартной библиотеки при их применении.Use standard library exception types when they apply. Наследовать пользовательские типы исключений от иерархии exception классов .Derive custom exception types from the exception Class hierarchy.

  • Не разрешать исключения для экранирования из деструкторов или функций освобождения памяти.Don't allow exceptions to escape from destructors or memory-deallocation functions.

Исключения и производительностьExceptions and performance

Механизм исключения имеет минимальные затраты на производительность, если исключение не создается.The exception mechanism has a minimal performance cost if no exception is thrown. При возникновении исключения стоимость прохода стека и его очистки приблизительно сравнима с затратами на вызов функции.If an exception is thrown, the cost of the stack traversal and unwinding is roughly comparable to the cost of a function call. Дополнительные структуры данных необходимы для контроля стека вызовов после того try , как был выполнен блок, и при возникновении исключения требуются дополнительные инструкции для очистки стека.Additional data structures are required to track the call stack after a try block is entered, and additional instructions are required to unwind the stack if an exception is thrown. Однако в большинстве случаев затраты на производительность и объем памяти не являются существенными.However, in most scenarios, the cost in performance and memory footprint isn't significant. Негативное воздействие исключений на производительность может быть значительным только для систем с ограниченным объемом памяти.The adverse effect of exceptions on performance is likely to be significant only on memory-constrained systems. Кроме того, в циклах, критических с точки зрения производительности, часто возникает ошибка, и существует тесная связь между кодом и его обработкой.Or, in performance-critical loops, where an error is likely to occur regularly and there's tight coupling between the code to handle it and the code that reports it. В любом случае невозможно понять фактическую стоимость исключений без профилирования и измерения.In any case, it's impossible to know the actual cost of exceptions without profiling and measuring. Даже в редких случаях, когда стоимость существенна, можно взвесить ее на более высокую правильность, упростить обслуживание и другие преимущества, предоставляемые хорошо спроектированной политикой исключений.Even in those rare cases when the cost is significant, you can weigh it against the increased correctness, easier maintainability, and other advantages that are provided by a well-designed exception policy.

Исключения и утвержденияExceptions versus assertions

Исключения и утверждения — это два отдельных механизма для обнаружения ошибок во время выполнения в программе.Exceptions and asserts are two distinct mechanisms for detecting run-time errors in a program. Используйте assert инструкции для проверки условий во время разработки, которые никогда не должны быть истинными, если весь код правильный.Use assert statements to test for conditions during development that should never be true if all your code is correct. Нет никакой точки в обработке такой ошибки с помощью исключения, так как эта ошибка указывает на то, что что-то в коде должно быть исправлено.There's no point in handling such an error by using an exception, because the error indicates that something in the code has to be fixed. Он не представляет условие, которое программа может восстанавливать из среды выполнения.It doesn't represent a condition that the program has to recover from at run time. assertОстанавливает выполнение на инструкции, чтобы можно было проверить состояние программы в отладчике.An assert stops execution at the statement so that you can inspect the program state in the debugger. Исключение продолжит выполнение из первого соответствующего обработчика catch.An exception continues execution from the first appropriate catch handler. Используйте исключения для проверки ошибок, которые могут возникнуть во время выполнения, даже если код правильный, например "файл не найден" или "недостаточно памяти".Use exceptions to check error conditions that might occur at run time even if your code is correct, for example, "file not found" or "out of memory." Исключения могут обработаны, даже если восстановление просто выводит сообщение в журнал и завершает программу.Exceptions can handle these conditions, even if the recovery just outputs a message to a log and ends the program. Всегда проверяйте аргументы для открытых функций с помощью исключений.Always check arguments to public functions by using exceptions. Даже если функция бесплатна, вы можете не иметь полного контроля над аргументами, которые пользователь может передать ему.Even if your function is error-free, you might not have complete control over arguments that a user might pass to it.

Исключения C++ и исключения SEH WindowsC++ exceptions versus Windows SEH exceptions

Программы C и C++ могут использовать механизм структурированной обработки исключений (SEH) в операционной системе Windows.Both C and C++ programs can use the structured exception handling (SEH) mechanism in the Windows operating system. Понятия SEH похожи на объекты в исключениях C++, за исключением того, что SEH __try использует __except конструкции, и, а __finally не try и catch .The concepts in SEH resemble the ones in C++ exceptions, except that SEH uses the __try, __except, and __finally constructs instead of try and catch. В компиляторе Microsoft C++ (КОМПИЛЯТОРОМ MSVC) исключения C++ реализуются для SEH.In the Microsoft C++ compiler (MSVC), C++ exceptions are implemented for SEH. Однако при написании кода C++ используйте синтаксис исключения C++.However, when you write C++ code, use the C++ exception syntax.

Дополнительные сведения о SEH см. в разделе структурированная обработка исключений (C/C++).For more information about SEH, see Structured Exception Handling (C/C++).

Спецификации исключений и noexceptException specifications and noexcept

Спецификации исключений были введены в C++ как способ указания исключений, которые может вызывать функция.Exception specifications were introduced in C++ as a way to specify the exceptions that a function might throw. Однако спецификации исключений выдают проблемы на практике и являются устаревшими в стандарте "черновик C++ 11".However, exception specifications proved problematic in practice, and are deprecated in the C++11 draft standard. Мы рекомендуем не использовать throw спецификации исключений throw() , кроме, что указывает, что функция не допускает исключений для экранирования.We recommend that you don't use throw exception specifications except for throw(), which indicates that the function allows no exceptions to escape. Если необходимо использовать спецификации исключений устаревшей формы throw( type-name ) , Поддержка компилятором MSVC ограничена.If you must use exception specifications of the deprecated form throw( type-name ), MSVC support is limited. Дополнительные сведения см. в разделе спецификации исключений (throw).For more information, see Exception Specifications (throw). noexcept Описатель вводится в c++ 11 в качестве предпочтительного варианта throw() .The noexcept specifier is introduced in C++11 as the preferred alternative to throw().

См. такжеSee also

Как взаимодействовать с исключительным и неисключительным кодомHow to: Interface between exceptional and non-exceptional code
Справочник по языку C++C++ language reference
Стандартная библиотека C++C++ Standard Library