AUTOSAR C++14 Rule A15-2-1

Конструкторы, которые не являются noexcept, не должны вызываться до запуска программы

Описание

Определение правила

Конструкторы, которые не являются noexcept, не должны вызываться до запуска программы.

Объяснение

На C++ компилятор отвечает на исключение следующими шагами:

  • Компилятор пытается сопоставить исключение с обработчиком в текущих возможностях или более высоких возможностей.

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

  • Если исключение не совпадает с обработчиком, то компилятор завершает выполнение определенным реализацией способом. То есть точный процесс завершения программы зависит от конкретного набора программного и оборудования, которое вы используете. Например, компилятор может вызвать std::terminate(), который, в свою очередь, может вызвать std::abort() для ненормального прекращения выполнения. На основе реализации, стек может не быть размотан, прежде чем программа будет прервана. Если стек не размотан до завершения программы, то деструкторы переменных в стеке не вызываются, что приводит к утечке ресурсов и уязвимостям безопасности.

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

class A{
	A(){
		
		//...
	}	
};

static A obj;

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

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

Реализация Polyspace

Polyspace® флаги, где non - noexcept непосредственно вызываются конструкторы статического или глобального объекта. В нем также подсвечиваются несовместимые конструкторы.

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

Если вы ожидаете нарушения правил, но не видите его, обратитесь к разделу «Стандартные нарушения кодирования не отображаются».

Примеры

расширить все

В этом примере показано, как Polyspace помечает конструкторы статических или глобальных объектов.

#include <cstdint>
#include <stdexcept>
#include <string>
class A
{
public:
	A() noexcept : x(0){}
	A(std::int32_t n) : x(n) {
		throw std::runtime_error("Unexpected error"); 
	}
	A(std::int32_t i, std::int32_t j) noexcept : x(i + j)
	{
		try {
			throw std::runtime_error("Error");
		}
		catch (std::exception& e) {
		}
	}
private:
	std::int32_t x;
};

static A a1;     // Compliant
static A a2(5);  // Noncompliant
static A a6(5);  // Ignored because unused
static A a3(5, 10); // Compliant
A a4(5);  //Noncompliant          
A a5(5, 10); //Compliant
int foo_A(A a) { };

int bar_A(int value) {
	A a{value};   //Compliant
	return foo_A(a);
}
int value2b = bar_A(20);   // Compliant
std::string s{"Hello World"};//Noncompliant
int value2a = foo_A(20);   //Noncompliant
int convert(){

	
	return  foo_A(a2);   
}

  • Polyspace помечает оператор std::string s{"Hello World"}; потому что этот оператор вызывает не- noexcept конструктор строкового s перед запуском программы.

  • Polyspace помечает оператор A a4(5); потому что этот оператор вызывает не- noexcept конструктор A(std::int32_t n) перед запуском программы.

  • Polyspace помечает оператор static A a2(5); потому что этот оператор вызывает не- noexcept конструктор A(std::int32_t n) перед запуском программы.

  • Polyspace помечает оператор int value2a = foo_A(20); потому что неявное преобразование из int на A требует вызова non- noexcept конструктор A(std::int32_t n) перед запуском.

  • Polyspace не помечает оператор static A a6(5);. Потому что a6 не используется в этом коде, компилятор не создает объект. В результате non - noexcept конструктор A(std::int32_t n) не вызывается до запуска программы.

  • Polyspace не помечает оператор A a{value}; в теле функции bar_A() потому что объект a является локальным и не создается во время запуска программы.

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

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