Практическое руководство. Создание и использование экземпляров unique_ptr

Unique_ptr не делится указателем. Его нельзя скопировать в другую unique_ptr, передать значение в функцию или использовать в любом алгоритме стандартной библиотеки C++, который требует создания копий. unique_ptr можно только переместить. Это означает, что владение ресурсов памяти переносится в другое unique_ptr и оригинал unique_ptr больше им не владеет. Рекомендуется ограничить объект одним владельцем, поскольку множественное владение усложняет логику программы. Таким образом, если вам нужен умный указатель для обычного объекта C++, используйте unique_ptrи при создании unique_ptrвспомогательной функции make_unique .

Следующая схема иллюстрирует передачу прав собственности между двумя экземплярами unique_ptr.

Diagram that shows moving the ownership of a unique pointer.

unique_ptr определяется в заголовке <memory> стандартной библиотеки C++. Это точно так же эффективно, как необработанный указатель и может использоваться в контейнерах стандартной библиотеки C++. Добавление unique_ptr экземпляров в контейнеры стандартной библиотеки C++ эффективно, так как конструктор unique_ptr перемещения устраняет необходимость в операции копирования.

Пример 1

В следующем примере описывается порядок создания экземпляров unique_ptr и передачи их между функциями.

unique_ptr<Song> SongFactory(const std::wstring& artist, const std::wstring& title)
{
    // Implicit move operation into the variable that stores the result.
    return make_unique<Song>(artist, title);
}

void MakeSongs()
{
    // Create a new unique_ptr with a new object.
    auto song = make_unique<Song>(L"Mr. Children", L"Namonaki Uta");

    // Use the unique_ptr.
    vector<wstring> titles = { song->title };

    // Move raw pointer from one unique_ptr to another.
    unique_ptr<Song> song2 = std::move(song);

    // Obtain unique_ptr from function that returns by value.
    auto song3 = SongFactory(L"Michael Jackson", L"Beat It");
}

В этих примерах демонстрируется эта базовая характеристика unique_ptr: ее можно изменить, но не для копирования. "Перемещение" перемещает владельца в новый unique_ptr и сбрасывает старый unique_ptr.

Пример 2

В следующем примере описывается порядок создания экземпляров unique_ptr и их использования в векторе.

void SongVector()
{
    vector<unique_ptr<Song>> songs;
    
    // Create a few new unique_ptr<Song> instances
    // and add them to vector using implicit move semantics.
    songs.push_back(make_unique<Song>(L"B'z", L"Juice")); 
    songs.push_back(make_unique<Song>(L"Namie Amuro", L"Funky Town")); 
    songs.push_back(make_unique<Song>(L"Kome Kome Club", L"Kimi ga Iru Dake de")); 
    songs.push_back(make_unique<Song>(L"Ayumi Hamasaki", L"Poker Face"));

    // Pass by const reference when possible to avoid copying.
    for (const auto& song : songs)
    {
        wcout << L"Artist: " << song->artist << L"   Title: " << song->title << endl; 
    }    
}

Обратите внимание, что в диапазоне для цикла unique_ptr передается по ссылке. При попытке передачи по значению компилятор выдаст ошибку, поскольку конструктор копирования unique_ptr удален.

Пример 3

В следующем примере показана инициализация unique_ptr, являющегося членом класса.


class MyClass
{
private:
    // MyClass owns the unique_ptr.
    unique_ptr<ClassFactory> factory;
public:

    // Initialize by using make_unique with ClassFactory default constructor.
    MyClass() : factory (make_unique<ClassFactory>())
    {
    }

    void MakeClass()
    {
        factory->DoSomething();
    }
};

Пример 4

Вы можете использовать make_unique для создания unique_ptr массива, но нельзя использовать make_unique для инициализации элементов массива.

// Create a unique_ptr to an array of 5 integers.
auto p = make_unique<int[]>(5);

// Initialize the array.
for (int i = 0; i < 5; ++i)
{
    p[i] = i;
    wcout << p[i] << endl;
}

Дополнительные примеры см. в make_unique.

См. также

Интеллектуальные указатели (современный C++)
make_unique