Main Content

Use of new or make_unique instead of more efficient make_shared

Using new or make_unique to initialize or reset shared_ptr results in additional memory allocation

Since R2021a

Description

This defect occurs when you use:

  • new or make_unique to initialize a shared_ptr instance. For example:

    std::shared_ptr<T> p1(new T());
    std::shared_ptr<T> p2(make_unique<T>());

  • new to reset a shared_ptr instance. For example:

    std::shared_ptr<T> p1;
    //...
    p1.reset(new T);

You use shared_ptr instances when you want multiple smart pointers to own and manage the same object. The instances also share a control block that holds a count of the number of instances that own the managed object.

Polyspace® does not flag the use of new to initialize a shared_ptr instance in private or protected constructors. For example, no defect is raised on the use of new in this code snippet:

class PrivateCTor
{
public:
    static std::shared_ptr<PrivateCTor> makeOne()
    {
        return std::shared_ptr<PrivateCTor>(new PrivateCTor);
    }
private:
    PrivateCTor();
};

Risk

When you use new or make_unique to initialize a shared_ptr instance, an additional allocation operation creates storage for the control block. The address of the additional allocation might be in a different memory page or outside the data-cache compared to the address of the managed object.

Fix

Use std::make_shared to initialize a shared_ptr instance. The function performs a single allocation operation with enough memory to store the managed object and the control block.

Performance improvements might vary based on the compiler, library implementation, and environment that you are using.

Examples

expand all

#include <memory>
#include <string>

struct Profile {
    virtual ~Profile() = default;
};

struct Player : public Profile {
    std::string name;
    std::int8_t rank;

    Player();
    Player(const std::string& name_, const std::int8_t& rank_) :
        name{ name_ }, rank{ rank_ } {}
};

void func()
{

    std::shared_ptr<Player> player1(new Player("Serena Williams", 1));
    std::shared_ptr<Player> top_rank(player1);

}

In this example, Polyspace flags the use of new to initialize player1. The program performs an additional allocation operation for the control block that holds the counter of all instances of shared_ptr that own the managed Player object.

Correction — Use std::make_shared to Initialize shared_ptr

One possible correction is to use std::make_shared to initialize player1.

#include <memory>
#include <string>

struct Profile {
    virtual ~Profile() = default;
};

struct Player : public Profile {
    std::string name;
    std::int8_t rank;

    Player();
    Player(const std::string& name_, const std::int8_t& rank_) :
        name{ name_ }, rank{ rank_ } {}
};

void func()
{

    auto player1 = std::make_shared<Player>("Serena Williams", 1);
    std::shared_ptr<Player> top_rank(player1);

} 

Result Information

Group: Performance
Language: C++
Default: Off
Command-Line Syntax: MISSING_MAKE_SHARED
Impact: Low

Version History

Introduced in R2021a