AUTOSAR C++14 Rule M15-3-1

Исключения должны быть повышены только после запуска и перед завершением

Описание

Управляйте определением

Исключения должны быть повышены только после запуска и перед завершением.

Объяснение

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

Если исключение повышено или во время фазы запуска или во время фазы завершения, вы не можете записать обработчик исключений, который компилятор может выполнить в тех фазах. Например, вы можете реализовать main() как function-try-catch блокируйтесь, чтобы обработать исключения. catch блоки в main() может обработать только исключения, повышенные в main(). Ни один из catch блоки могут обработать исключения, повышенные во время фазы запуска или завершения. Когда такие исключения повышены, компилятор может неправильно отключить выполнение кода, не раскручивая стек. Рассмотрите этот код где конструкция и разрушение статического объекта obj может вызвать исключение.

class A{
	A(){throw(0);}
	~A(){throw(0)}	
};

static A obj;

main(){
	//...
}
Статический объект obj создается путем вызова A() перед main() запускается, и это уничтожается путем вызова ~A() после main() концы. Когда A() или ~A() повышает исключение, обработчик исключений не может быть соответствующим им. На основе реализации такое исключение может привести к завершению программы без раскручивания стека, ведя к утечке памяти и уязвимостям системы обеспечения безопасности.

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

Реализация Polyspace

Polyspace® отмечает глобальную переменную или объявление статической переменной, которое использует вызываемую сущность, которая может повысить исключение. Например:

  • Функция: Когда вы вызываете функцию инициализатора или конструктора непосредственно, чтобы инициализировать глобальную или статическую переменную, проверки Polyspace, повышает ли функция исключение и отмечает объявление переменной если функциональное повышение силы исключение. Polyspace выводит ли функциональное повышение силы исключение независимо от его спецификации исключения. Например, если noexcept конструктор повышает исключение, Polyspace отмечает его. Если инициализатор или конструктор вызывают другую функцию, Polyspace принимает, что вызванная функция может повысить исключение, только если это задано как noexcept(<false>). Некоторые стандартные библиотечные функции, такие как конструктор std::string, используйте указатели на функции, чтобы выполнить выделение памяти, которое может повысить исключения. Polyspace не отмечает объявление переменной, когда эти функции используются.

  • Внешняя функция: Когда вы вызываете внешние функции, чтобы инициализировать глобальную или статическую переменную, Polyspace отмечает объявление, если внешняя функция задана как noexcept(<false>).

  • Виртуальная функция: Когда вы вызываете виртуальную функцию, чтобы инициализировать глобальную или статическую переменную, Polyspace отмечает его, если виртуальная функция задана как noexcept(<false>) в любом производном классе. Например, если вы используете виртуальную функцию инициализатора, которая объявляется как noexcept(<true>) в базовом классе и noexcept(<false>) в последующем производном классе Polyspace отмечает его.

  • Указатели на функцию: Когда вы используете указатель на функцию, чтобы инициализировать глобальную или статическую переменную, Polyspace принимает, что указатель на функцию не повышает исключения.

Polyspace игнорирует:

  • Исключения повышены в деструкторах

  • Исключения повышены в atexit() операции

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

Поиск и устранение проблем

Если вы ожидаете нарушение правила, но не видите его, обратитесь к Кодированию Стандартных Нарушений, Не Отображенных.

Примеры

развернуть все

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

#include <stdexcept>
#include <string>
class C
{
public:
	C ( ){throw ( 0 );}
	~C ( ){throw ( 0 );}
};
int LibraryFunc();                                    
int LibraryFunc_noexcept_false() noexcept(false);     
int LibraryFunc_noexcept_true() noexcept(true);       
int  g() noexcept {                                   
	throw std::runtime_error("dead code");
	return 0;
}
int f() noexcept {                                    
	return g();                                         
}
int init(int a) {
	if (a>10) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void* alloc(size_t s) noexcept {          
	return new int[s];
}
int a = LibraryFunc() + 
LibraryFunc_noexcept_true();            // Compliant
int global_int =
LibraryFunc_noexcept_false() +          // Noncompliant
LibraryFunc_noexcept_true();
static C static_c;                      //Noncompliant
static C static_d;                      //Compliant
C &get_static_c(){
	return static_c;
}
C global_c;                             //Noncompliant 
int a3 = f();                           //Compliant
int b3 = g();                           //Noncompliant
int a4 = init(5);                       //Noncompliant
int b5 = init(20);                      //Noncompliant
int* arr = (int*)alloc(5);              //Noncompliant

int main(){
	//...
}

  • Глобальный указатель arr инициализируется при помощи функционального alloc(). Поскольку alloc() использование new чтобы выделить память, это может повысить исключение при инициализации arr во время запуска программы. Polyspace отмечает объявление arr и подсвечивает использование new в функциональном alloc().

  • Целочисленная переменная b3 инициализируется путем вызова функционального g(), который задан как noexcept. Polyspace выводит что правильная спецификация исключения g() noexcept(false) потому что это содержит throw() оператор. Инициализация глобальной переменной b3 при помощи g() может повысить исключение при инициализации arr во время запуска программы. Polyspace отмечает объявление b3 и подсвечивает throw оператор в g(). Объявление a3 путем вызова f() не отмечается. Поскольку f() noexcept функция, которая не выдает и вызывает другой noexcept функция, Polyspace выводит тот f() не повышает исключение.

  • Глобальные переменные a4 и b5 инициализируются путем вызова функционального init(). Функциональный init() может повысить исключение в определенных случаях, в зависимости от контекста. Поскольку Polyspace выводит спецификацию исключения функции статически, это принимает тот init() может повысить исключение независимо от контекста. Следовательно, Polyspace отмечает объявления обоих a4 и b5, даже при том, что init() повышает исключение только при инициализации b5.

  • Глобальная переменная global_int инициализируется путем вызывания двух внешних функций. Внешний функциональный LibraryFunc_noexcept_false() задан как noexcept(false) и Polyspace принимает, что эта внешняя функциональная сила повышает исключение. Polyspace отмечает объявление global_int. Polyspace не отмечает объявление a потому что это инициализируется вызовом внешних функций, который не задан как noexcept(false).

  • Статическая переменная static_c и нестатическая глобальная переменная global_cобъявляется и инициализируется при помощи конструктора класса C, который может повысить исключение. Polyspace отмечает объявления этих переменных и подсвечивает throw() оператор в конструкторе класса C. Polyspace не отмечает объявление неиспользованной статической переменной static_d, даже при том, что его конструктор может повысить исключение. Поскольку это не использовано, static_d не инициализируется и его конструктор не называется. Его объявление не повышает исключения.

Проверяйте информацию

Группа: Обработка исключений
Категория: необходимый, автоматизированный