﻿# V824\. It is recommended to use the 'make\_unique/make\_shared' function to create smart pointers\.

Анализатор рекомендует создать умный указатель не путем вызова конструктора, принимающего "сырой" указатель на ресурс, а вызовом функции 'make\_unique' / 'make\_shared'\.

Использование этих функций позволяет:

* улучшить читаемость кода, устраняя явные вызовы оператора 'new' для динамических аллокаций \(сами умные указатели устраняют явный вызов оператора 'delete'\);
* повысить безопасность кода в случае броска исключения;
* может оптимизировать размещение объектов в памяти\.

Рассмотрим пример кода:

```cpp
void foo(std::unique_ptr<int> a, std::unique_ptr<int> b)
{
  ....
}

void bar()
{
  foo( std::unique_ptr<int> { new int { 0 } },
       std::unique_ptr<int> { new int { 1 } });
}
```

Поскольку стандартом не регламентируется порядок вычисления аргументов функции, компилятор в целях оптимизации может сделать это в такой последовательности:

1. Вызов 'new int \{ 0 \}'
1. Вызов 'new int \{ 1 \}'
1. Первый вызов конструктора 'std::unique\_ptr<int\>'
1. Второй вызов конструктора 'std::unique\_ptr<int\>'

В этом случае, если второй вызов 'new' бросит исключение, произойдет утечка памяти – ресурс, выделенный первым вызовом 'new', никогда не будет освобожден\. Создание указателя при помощи 'make\_unique' решает эту проблему, гарантируя освобождение памяти в случае броска исключения\. 

Оптимизированный код:

```cpp
void foo(std::unique_ptr<int> a, std::unique_ptr<int> b)
{
  ....
}

void bar()
{
  foo( std::make_unique<int>(0), std::make_unique<int>(1));
}
```

Начиная с C\+\+17, хотя порядок вычисления аргументов остается неуточненным, вводятся дополнительные гарантии\. Все побочные эффекты от аргумента функции должны быть вычислены до того, как произойдет вычисление следующего аргумента\. Это снижает риски в случае исключений, но использовать 'make\_unique' все равно предпочтительно\.

Замечание по поводу 'make\_shared'\. При использовании этой функции контрольный блок указателя размещается в памяти рядом с объектом\. Это уменьшает количество динамических аллокаций и оптимизирует использование кэша процессора\. 

Объект удаляется, когда счетчик ссылок становится нулевым, но контрольный блок существует до тех пор, пока существуют слабые ссылки на указатель\. Если контрольный блок и объект были созданы при помощи 'make\_shared' \(т\.е\. размещены в одной области памяти\), это приводит к тому, что память не может быть освобождена до тех пор, пока счетчик ссылок нулевой и на объект ссылается хотя бы один 'weak\_ptr'\. Для больших объектов такое поведение может быть нежелательным\. Если функция 'make\_shared' не используется сознательно, чтобы избежать размещения контрольного блока в одной области памяти с объектом, срабатывание диагностики можно [подавить](https://pvs-studio.ru/ru/docs/manual/0017/)\.

Ограничение, связанное с разными версиями стандарта C\+\+: так как возможности функций '[make\_unique](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)' и '[make\_shared](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared)' менялись, начиная с C\+\+11, диагностика зависит от версии стандарта следующим образом:

1. C\+\+11: анализатор предлагает заменять аллокацию объекта и его последующую передачу в конструктор 'shared\_ptr' на функцию 'make\_shared'\.
1. C\+\+14 или выше: анализатор дополнительно предлагает заменять аллокацию одного объекта или массива объектов на функцию 'make\_unique'\.
1. C\+\+20 или выше: анализатор дополнительно предлагает заменить конструктор 'shared\_ptr' на функцию 'make\_shared' также и для массива объектов\.