Main Content

MISRA C++:2023 Rule 23.11.1

The raw pointer constructors of std::shared_ptr and std::unique_ptr should not be used

Since R2024b

Description

Rule Definition

The raw pointer constructors of std::shared_ptr and std::unique_ptr should not be used.

Rationale

Instead of allocating memory by using the new operator and converting the resulting raw pointer to an std::unique_ptr or std::shared_ptr object, for instance:

class numberClass {
   public:
     numberClass(int n): number(n){}
   private: 
     int number;
}
int aNumber=1; 
std::unique_ptr<numberClass> numberUniquePtr (new numberClass(aNumber));
std::shared_ptr<numberClass> numberSharedPtr (new numberClass(aNumber));
Create a std::unique_ptr or std::shared_ptr object directly using the std::make_unique or std::make_shared function. For instance:
auto numberUniquePtr = std::make_unique<numberClass>(aNumber);
auto numberSharedPtr = std::make_shared<numberClass>(aNumber);

Using std::make_unique or std::make_shared is preferred because:

  • The creation of the std::unique_ptr object using std::make_unique or std::shared_ptr object using std::make_shared is exception-safe and improves run-time performance. For instance, if dynamic memory allocation with the new operator and the subsequent conversion happened in separate steps, an exception can occur between the steps, leading to a memory leak.

  • You can use a more concise syntax. You do not have to repeat the data type of the object that is dynamically allocated.

Polyspace Implementation

The checker flags:

  • The creation of an std::unique_ptr object (or boost::unique_ptr object) from the raw pointer returned by the new operator.

  • The creation of an std::shared_ptr object (or boost::shared_ptr object) from the raw pointer returned by the new operator.

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 <string>

class Record {
    public:
       Record(std::string aName, std::string someInfo): name(aName), info(someInfo){       
       }
    private:
       std::string name;
       std::string info;
};

void addRecordToDataBase(std::unique_ptr<Record> aRecord, bool dataBaseIsFull);
bool checkDataBase(void); //Checks if database can accept more entries

void createRecord() {
    std::unique_ptr<Record> recordPtr1 (new Record("John", "Mechanic, New York")); //Noncompliant
    auto recordPtr2 = std::make_unique<Record>("Tom", "Plumber, Boston"); //Compliant
    
    addRecordToDataBase(std::unique_ptr<Record>(new Record("John", "Mechanic, New York")), checkDataBase()); //Noncompliant
    addRecordToDataBase(std::make_unique<Record>("Tom", "Plumber, Boston"), checkDataBase()); //Compliant
}

In this example, the rule is violated whenever the new operator returns a raw pointer and a subsequent conversion creates an std::unique_ptr object from the raw pointer.

In the first call to addRecordToDatabase, the following sequence of events can occur (prior to C++17):

  • First argument evaluation. The new operator dynamically allocates memory for a Record object.

  • Second argument evaluation. The function checkDataBase is called.

  • Continuation of first argument evaluation. The raw pointer obtained from the new operator is converted to an std::unique_ptr object.

If the second step throws an exception, the raw pointer is not assigned to the resource management object and results in a memory leak.

#include<memory>
#include <cstdint>
#include <string>

class Record {
public:
	Record() = default;
	Record(std::string aName, std::string someInfo): name(aName), info(someInfo){       
	}
private:
	std::string name;
	std::string info;
};

void addRecordToDataBase(std::shared_ptr<Record> aRecord, bool dataBaseIsFull);
bool checkDataBase(void); //Checks if database can accept more entries

void createRecord() {
	std::shared_ptr<Record> recordPtr1 (new Record("John", "Mechanic, New York")); //Noncompliant
	auto recordPtr2 = std::make_shared<Record>("Tom", "Plumber, Boston"); //Compliant
	//...
	addRecordToDataBase(std::shared_ptr<Record>(new Record("John", "Mechanic, New York")), checkDataBase()); //Noncompliant
	addRecordToDataBase(std::make_shared<Record>("Tom", "Plumber, Boston"), checkDataBase()); //Compliant
	//...
}

std::shared_ptr<Record> getSP(void) {
	std::shared_ptr<Record> result{std::make_shared<Record>()};//Compliant 
	return result;                                                      
}

In this example, the rule is violated whenever the new operator returns a raw pointer and a subsequent conversion creates an std::shared_ptr object from the raw pointer.

In the first call to addRecordToDatabase, the following sequence of events can occur (prior to C++17):

  • First argument evaluation. The new operator dynamically allocates memory for a Record object.

  • Second argument evaluation. The function checkDataBase is called.

  • Continuation of first argument evaluation. The raw pointer obtained from new is converted to an std::shared_ptr object.

If the second step throws an exception, the raw pointer is not assigned to the resource management object and results in a memory leak. Polyspace flags the first call to addRecordToDatabase.

In the function getSP(), the shared pointer result is copy constructed from a temporary shared_ptr object. An implicit copying of the shared pointer also takes place during the return statement. Polyspace does not flag these operations.

Check Information

Group: General utilities library
Category: Advisory

Version History

Introduced in R2024b