Практическое руководство. Создание и использование экземпляров unique_ptr
unique_ptr не предоставляет общий доступ к своему указателю. Его невозможно скопировать в другой unique_ptr, передать по значению функции или использовать в любом алгоритме стандартной библиотеки шаблонов (STL), предполагающем создание копий. unique_ptr можно только переместить. Это означает, что владение ресурсов памяти переносится в другое unique_ptr и оригинал unique_ptr больше им не владеет. Рекомендуется ограничить объект одним владельцем, поскольку множественное владение усложняет логику программы. Поэтому при необходимости интеллектуального указателя для простого объекта C++ используйте unique_ptr и при построении unique_ptr используйте вспомогательную функцию make_unique.
Следующая схема иллюстрирует передачу прав собственности между двумя экземплярами unique_ptr.
unique_ptr определяется в заголовке <memory> в STL. Он так же эффективен, как и необработанный указатель, и может использоваться в контейнерах STL. Добавление экземпляров unique_ptr к контейнерам STL эффективно, так как конструктор unique_ptr исключает необходимость перемещения для копирования.
Пример
В следующем примере описывается порядок создания экземпляров 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.
В следующем примере описывается порядок создания экземпляров 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 удален.
В следующем примере показана инициализация 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();
}
};
Функцию 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.