Main Content

AUTOSAR C++14 Rule A15-1-4

If a function exits with an exception, then before a throw, the function shall place all objects/resources that the function constructed in valid states or it shall delete them.

Since R2021b

Description

Rule Definition

If a function exits with an exception, then before a throw, the function shall place all objects/resources that the function constructed in valid states or it shall delete them.

Rationale

When a function exits with an exception, any resource or memory that the function allocated might not be properly deallocated. Consider this code:

FILE* FilePtr; 
//...
void foo(){
	FilePtr = fopen("some_file.txt", "r");
//...
	if(/*error condition*/)
	throw ERROR_CODE;
	
	delete FilePtr;
}
The allocated file pointer is intended to be deallocated before the function finishes execution. But when an exception takes place, the function exits without deleting the pointer, which results in a memory leak. To avoid memory leaks, a function must set all resources that it allocates to a valid state before it goes out of scope. For instance, in the preceding code example, the function must delete the pointer FilePtr before the throw statement.

Instead of manually tracking the allocation and deallocation of resources, the best practice is to follow the "Resource Acquisition Is Initialization" (RAII) or the "Constructor Acquires, Destructor Releases" (CADre) design pattern. In this pattern, resource allocation is performed in constructors and resource deallocation is performed in destructors. The lifecycle of resources are controlled by scope-bound objects in this pattern. When functions reach the end of their scope, the acquired resources are properly released. Consider this code:

void releaseFile(std::FILE* fp) { std::fclose(fp); }
std::unique_ptr<std::FILE, decltype(&releaseFile)> FilePtr;
//...
void foo(){
	FilePtr(std::fopen("some_file.txt"),&releaseFile);
//...
	if(/*error condition*/)
	throw ERROR_CODE;
}
Here, the unique pointer FilePTR invokes the function releaseFile to delete the allocated resource once the function foo reaches the end of its scope, whether normally or because of an unhandled exception.

C++ smart pointers such as std::unique_ptr and std::shared_ptr follow the RAII pattern. They simplify managing the lifecycle of resources during exception handling. Avoid using raw pointers whenever possible.

Polyspace Implementation

Polyspace® flags an uncaught throw statement in a 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 throw statement in a catch block is flagged if the resources are not deallocated before raising the exception.

Polyspace does not flag a throw statement if it is within a try block that has an appropriate handler or if the exception is raised before allocating resources.

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 <cstdint>
#include <memory>
#include <stdexcept>
extern int sensorFlag() noexcept;
namespace Noncompliant{
	void func(){
		int* intPtr = new int;
		int data = sensorFlag();
		if(data==-1)//Error
		throw std::runtime_error("Unexpected value");//Noncompliant
		//...
		delete intPtr;
	}
}
namespace Compliant{
	void func(){
		int* intPtr = new int;
		int data = sensorFlag();
		if(data==-1){//Error
			delete intPtr;
			throw std::runtime_error("Unexpected value");//Compliant
		}
		//...
		delete intPtr;
	}
}
namespace BestPractice{
	void func(){
		std::unique_ptr<int> intPtr = std::make_unique<int>();
		int data = sensorFlag();
		if(data==-1){//Error
			throw std::runtime_error("Unexpected value");//Compliant
		}
		//...
		
	}
}

In this example, the function Noncompliant::func() manages the raw pointer inPtr. The function allocates memory for it, and then releases the memory after some operations. The function exits with an exception when data is -1. In this case, the function exits before releasing the allocated memory, resulting in a memory leak. To prevent memory leak, the allocated memory must be released before raising the exception, as shown in Compliant::func.

The best practice is to follow the RAII design pattern and use unique_ptr instead of a raw pointer. BestPractice::func shows an implementation of func that follows the RAII pattern. In this case, the memory lifecycle is managed by the object itself. That is, once func is out of scope, the smart pointer intPtr deletes itself and releases the memory. Because the memory management is performed correctly by the smart pointer, BestPractice::func is simpler and safer.

Version History

Introduced in R2021b