Main Content

AUTOSAR C++14 Rule A15-2-2

If a constructor is not noexcept and the constructor cannot finish object initialization, then it shall deallocate the object's resources and it shall throw an exception

Since R2021a

Description

Rule Definition

If a constructor is not noexcept and the constructor cannot finish object initialization, then it shall deallocate the object's resources and it shall throw an exception.

Rationale

When a constructor abruptly terminates due to unhandled exception or failed dynamic resource allocation, it might leave some objects in a partially constructed object, which is undefined behavior. Before raising exceptions in class constructors, deallocate the already allocated resources. When allocating resources, specify the new operation as std::nothrow. Alternatively, perform the resource allocation in a try or function-try block to handle exceptions that might arise from a failed allocation.

Polyspace Implementation

Polyspace® flags a throw or new statement outside a try block in a non-noexcept class constructor if the statement might result in resource leak. For instance:

  • A throw statement outside a try block is flagged if the allocated resources are not deallocated before the statement.

  • A new statement is flagged if there are more than one new statement in succession and the latter ones is not specified as std::nothrow or wrapped in a try or function-try block.

Polyspace ignores classes that remain unused in your code.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include<cstdlib>
#include<exception>
 class complex_ptr{
	
	complex_ptr(){
		real = (double*)malloc(sizeof(double));
		imag = (double*)malloc(sizeof(double));
		if(real==nullptr || imag==nullptr){
			throw std::exception(); //Noncompliant
		}
	}
	~complex_ptr(){
		free(real);
		free(imag);
	}
	private:
	double* real;
	double* imag;
	
};
class complex_ptr2{
	
	complex_ptr2() {
		real = (double*)malloc(sizeof(double));
		imag = (double*)malloc(sizeof(double));
		if(real==nullptr || imag==nullptr){
			deallocate();
			throw std::exception(); //Compliant
		}
	}
	void deallocate(){
		free(real);
		free(imag);
	}
	~complex_ptr2(){
		deallocate();
	}
	private:
	double* real;
	double* imag;
	
};
void foo(void){
	complex_ptr Z;
	complex_ptr2 X;
	//...
}

In this example, the class complex_ptr is responsible for allocating and deallocating two raw pointers to double. The constructor complex_ptr::complex_ptr() terminates with an exception when a memory allocation operation fails. The class goes out of scope before deallocating the already allocated resources, resulting in a partially constructed object. Polyspace flags the throw statement in the constructor.

Similar to complex_ptr, the constructor of class complex_ptr2 raises an exception when a memory allocation operation fails. Before raising the exception, the constructor deallocates the allocated memory by calling deallocate(). This constructor is compliant with this rule.

#include<cstdlib>
#include <stdexcept>
#include <new>
 class complex_ptr{
	
	complex_ptr(): real(new double), imag(new double){ //Noncompliant		
		
	}
	~complex_ptr(){
		delete real;
		delete imag;
	}
	private:
	double* real;
	double* imag;
	
};

class complex_ptr2{
	
	complex_ptr2() try: real(new double), imag(new double){	//Compliant	
		
	}catch(std::bad_alloc){
		//...
	}
	~complex_ptr2(){
		delete real;
		delete imag;
	}
	private:
	double* real;
	double* imag;
	
};

void foo(void){
	complex_ptr Z;
	complex_ptr2 X;
	//...
}

In this example, the constructor of complex_ptr performs new operations that might raise exceptions. Because the constructor has no mechanism for handling these exceptions, they might cause the constructor to abruptly terminate. Such termination might leave the object in a partially defined state because the allocated resources are not deallocated. Polyspace flags the constructor. The constructor of complex_ptr2 performs the new operations in a function-try block and handles potential exceptions in a catch block. This constructor is compliant with the rule because it handles the exceptions that might arise from the new operations.

Check Information

Group: Exception handling
Category: Required, Partially automated

Version History

Introduced in R2021a