跳到主要内容

std::unique_ptr

定义于头文件 <memory>

声明

template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;

template <
class T,
class Deleter
> class unique_ptr<T[], Deleter>;

描述

std::unique_ptr是一个智能指针,它通过指针拥有并管理另一个对象,并在unique_ptr超出作用域时处置该对象。

当以下任一情况发生时,使用关联的删除器处置对象:

  • 管理unique_ptr对象被销毁

  • 管理unique_ptr对象通过操作符=reset()被分配了另一个指针

通过调用get_deleter()(ptr),使用潜在的用户提供的删除器处置对象。默认删除器使用delete操作符,它销毁对象并解除分配内存。

unique_ptr也可以不拥有任何对象,在这种情况下它被称为空的

std::unique_ptr有两个版本:

  • 管理单个对象(例如,用new分配);
  • 管理动态分配的对象数组(例如,用new[]分配)。

该类满足MoveConstructible和MoveAssignable的要求,但不满足CopyConstructible和CopyAssignable的要求。

类型要求

删除器必须是FunctionObject或FunctionObject的左值引用,或函数的左值引用,可使用unique_ptr::pointer类型的参数调用。

备注

只有非const的unique_ptr可以将管理对象的 ownership 转移给另一个unique_ptr。如果一个对象的生命周期由const std::unique_ptr管理,则其生命周期仅限于指针创建的作用域。

std::unique_ptr通常用于管理对象的生命周期,包括:

  • 通过保证在正常退出和通过异常退出时都进行删除,为处理具有动态生命周期的对象的类和函数提供异常安全性;
  • 将唯一拥有的、具有动态生命周期的对象的 ownership 传递给函数;
  • 从函数中获取唯一拥有的、具有动态生命周期的对象的 ownership;
  • 作为支持移动的容器(如std::vector)中的元素类型,它们持有指向动态分配对象的指针(例如,如果需要多态行为)。

std::unique_ptr可以为不完全类型T构造,例如为了方便在pImpl惯用法中作为句柄使用。如果使用默认删除器,则T必须在删除器被调用的代码点处是完整的,这发生在std::unique_ptr的析构函数、移动赋值操作符和reset成员函数中。(相反,std::shared_ptr不能从指向不完全类型的原始指针构造,但可以在T不完全的情况下销毁)。请注意,如果T是类模板特化,则使用unique_ptr作为操作数,例如!p,由于ADL,要求T的参数是完整的。

如果T是某个基类B的派生类,则std::unique_ptr可隐式转换为std::unique_ptr。结果std::unique_ptr的默认删除器将使用B的delete操作符,除非B的析构函数是虚函数,否则会导致未定义行为。请注意,std::shared_ptr行为不同:std::shared_ptr将使用类型T的delete操作符,并且即使B的析构函数不是虚函数,所拥有的对象也将被正确删除。

std::shared_ptr不同,std::unique_ptr可以通过满足NullablePointer的任何自定义句柄类型来管理对象。这允许,例如,通过提供一个定义了typedef boost::offset_ptr pointer的删除器来管理位于共享内存中的对象;或另一个花式指针。

特性测试宏标准注释
__cpp_lib_constexpr_memory202202L(C++23)constexpr std::unique_ptr

成员类型

公共成员pointer如果存在,则为std::remove_reference::type::pointer,否则为T*。必须满足NullablePointer
公共成员element_typeT,此unique_ptr管理的对象类型
公共成员deleter_typeDeleter,要从析构函数调用的函数对象或函数或函数对象的左值引用

成员函数

公共成员(构造函数)构造一个新的unique_ptr
(函数模板)
公共成员(析构函数)如果存在,则销毁管理的对象
(函数模板)
公共成员operator=赋值unique_ptr
(函数模板)

修改器

公共成员release返回指向管理对象的指针并释放 ownership
(函数模板)
公共成员reset替换托管对象
(函数模板)
公共成员swap交换托管对象
(函数模板)

观察者

公共成员get返回指向管理对象的指针
(函数模板)
公共成员get_deleter返回用于销毁管理对象的删除器
(函数模板)
公共成员operator bool检查是否存在关联的管理对象
(函数模板)

单对象版本,unique_ptr

公共成员operator*
operator->
解引用指向管理对象的指针
(函数模板)

数组版本,unique_ptr

公共成员operator[]提供对管理数组的索引访问
(函数模板)

非成员函数

公共成员make_unique (C++14)
make_unique_for_overwrite (C++20)
创建一个管理新对象的unique pointer
(函数模板)
公共成员operator==
operator!= (C++20 中移除)
operator<
operator<=
operator>
operator>>=
operator<=> (C++20)
与另一个unique_ptr或nullptr进行比较
(函数模板)
公共成员operator<< (std::unique_ptr)(C++20)将管理指针的值输出到输出流
(函数模板)
公共成员std::swap(std::unique_ptr)(C++11)特化 std::swap 算法
(函数模板)

帮助类

公共成员std::hash(std::unique_ptr)(C++11)std::unique_ptr的哈希支持
(函数模板)

示例

#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>

// helper class for runtime polymorphism demo below
struct B
{
virtual ~B() = default;

virtual void bar() {
std::cout
<< "B::bar\n";
}
};

struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }

void bar() override {
std::cout
<< "D::bar\n"; }
};

// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}

// helper function for the custom deleter demo below
void close_file(std::FILE* fp)
{
std::fclose(fp);
}

// unique_ptr-based linked list demo
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
};

std::unique_ptr<Node> head;

~List()
{
// destroy list nodes sequentially in a loop, the default destructor
// would have invoked its `next`'s destructor recursively, which would
// cause stack overflow for sufficiently large lists.
while (head)
head = std::move(head->next);
}

void push(int data)
{
head = std::unique_ptr<Node>(new Node{data, std::move(head)});
}
};

int main()
{
std::cout << "1) Unique ownership semantics demo\n";
{
// Create a (uniquely owned) resource
std::unique_ptr<D> p = std::make_unique<D>();

// Transfer ownership to `pass_through`,
// which in turn transfers ownership back through the return value
std::unique_ptr<D> q = pass_through(std::move(p));

// p is now in a moved-from 'empty' state, equal to nullptr
assert(!p);
}

std::cout << "\n" "2) Runtime polymorphism demo\n";
{
// Create a derived resource and point to it via base type
std::unique_ptr<B> p = std::make_unique<D>();

// Dynamic dispatch works as expected
p->bar();
}

std::cout << "\n" "3) Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // prepare the file to read
{
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
if (fp)
std::cout << char(std::fgetc(fp.get())) << '\n';
} // `close_file()` called here (if `fp` is not null)

std::cout << "\n" "4) Custom lambda-expression deleter and exception safety demo\n";
try
{
std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
});

throw std::runtime_error(""); // `p` would leak here if it were a plain pointer
}
catch (const std::exception&) { std::cout << "Caught exception\n"; }

std::cout << "\n" "5) Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p(new D[3]);
} // `D::~D()` is called 3 times

std::cout << "\n" "6) Linked list demo\n";
{
List wall;
for (int beer = 0; beer != 1'000'000; ++beer)
wall.push(beer);

std::cout << "1'000'000 bottles of beer on the wall...\n";
} // destroys all the beers
}
可能结果
1) Unique ownership semantics demo
D::D
D::bar
D::~D

2) Runtime polymorphism demo
D::D
D::bar
D::~D

3) Custom deleter demo
x

4) Custom lambda-expression deleter and exception safety demo
D::D
destroying from a custom deleter...
D::~D
Caught exception

5) Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D

6) Linked list demo
1'000'000 bottles of beer on the wall...

std::unique_ptr

定义于头文件 <memory>

声明

template<
class T,
class Deleter = std::default_delete<T>
> class unique_ptr;

template <
class T,
class Deleter
> class unique_ptr<T[], Deleter>;

描述

std::unique_ptr是一个智能指针,它通过指针拥有并管理另一个对象,并在unique_ptr超出作用域时处置该对象。

当以下任一情况发生时,使用关联的删除器处置对象:

  • 管理unique_ptr对象被销毁

  • 管理unique_ptr对象通过操作符=reset()被分配了另一个指针

通过调用get_deleter()(ptr),使用潜在的用户提供的删除器处置对象。默认删除器使用delete操作符,它销毁对象并解除分配内存。

unique_ptr也可以不拥有任何对象,在这种情况下它被称为空的

std::unique_ptr有两个版本:

  • 管理单个对象(例如,用new分配);
  • 管理动态分配的对象数组(例如,用new[]分配)。

该类满足MoveConstructible和MoveAssignable的要求,但不满足CopyConstructible和CopyAssignable的要求。

类型要求

删除器必须是FunctionObject或FunctionObject的左值引用,或函数的左值引用,可使用unique_ptr::pointer类型的参数调用。

备注

只有非const的unique_ptr可以将管理对象的 ownership 转移给另一个unique_ptr。如果一个对象的生命周期由const std::unique_ptr管理,则其生命周期仅限于指针创建的作用域。

std::unique_ptr通常用于管理对象的生命周期,包括:

  • 通过保证在正常退出和通过异常退出时都进行删除,为处理具有动态生命周期的对象的类和函数提供异常安全性;
  • 将唯一拥有的、具有动态生命周期的对象的 ownership 传递给函数;
  • 从函数中获取唯一拥有的、具有动态生命周期的对象的 ownership;
  • 作为支持移动的容器(如std::vector)中的元素类型,它们持有指向动态分配对象的指针(例如,如果需要多态行为)。

std::unique_ptr可以为不完全类型T构造,例如为了方便在pImpl惯用法中作为句柄使用。如果使用默认删除器,则T必须在删除器被调用的代码点处是完整的,这发生在std::unique_ptr的析构函数、移动赋值操作符和reset成员函数中。(相反,std::shared_ptr不能从指向不完全类型的原始指针构造,但可以在T不完全的情况下销毁)。请注意,如果T是类模板特化,则使用unique_ptr作为操作数,例如!p,由于ADL,要求T的参数是完整的。

如果T是某个基类B的派生类,则std::unique_ptr可隐式转换为std::unique_ptr。结果std::unique_ptr的默认删除器将使用B的delete操作符,除非B的析构函数是虚函数,否则会导致未定义行为。请注意,std::shared_ptr行为不同:std::shared_ptr将使用类型T的delete操作符,并且即使B的析构函数不是虚函数,所拥有的对象也将被正确删除。

std::shared_ptr不同,std::unique_ptr可以通过满足NullablePointer的任何自定义句柄类型来管理对象。这允许,例如,通过提供一个定义了typedef boost::offset_ptr pointer的删除器来管理位于共享内存中的对象;或另一个花式指针。

特性测试宏标准注释
__cpp_lib_constexpr_memory202202L(C++23)constexpr std::unique_ptr

成员类型

公共成员pointer如果存在,则为std::remove_reference::type::pointer,否则为T*。必须满足NullablePointer
公共成员element_typeT,此unique_ptr管理的对象类型
公共成员deleter_typeDeleter,要从析构函数调用的函数对象或函数或函数对象的左值引用

成员函数

公共成员(构造函数)构造一个新的unique_ptr
(函数模板)
公共成员(析构函数)如果存在,则销毁管理的对象
(函数模板)
公共成员operator=赋值unique_ptr
(函数模板)

修改器

公共成员release返回指向管理对象的指针并释放 ownership
(函数模板)
公共成员reset替换托管对象
(函数模板)
公共成员swap交换托管对象
(函数模板)

观察者

公共成员get返回指向管理对象的指针
(函数模板)
公共成员get_deleter返回用于销毁管理对象的删除器
(函数模板)
公共成员operator bool检查是否存在关联的管理对象
(函数模板)

单对象版本,unique_ptr

公共成员operator*
operator->
解引用指向管理对象的指针
(函数模板)

数组版本,unique_ptr

公共成员operator[]提供对管理数组的索引访问
(函数模板)

非成员函数

公共成员make_unique (C++14)
make_unique_for_overwrite (C++20)
创建一个管理新对象的unique pointer
(函数模板)
公共成员operator==
operator!= (C++20 中移除)
operator<
operator<=
operator>
operator>>=
operator<=> (C++20)
与另一个unique_ptr或nullptr进行比较
(函数模板)
公共成员operator<< (std::unique_ptr)(C++20)将管理指针的值输出到输出流
(函数模板)
公共成员std::swap(std::unique_ptr)(C++11)特化 std::swap 算法
(函数模板)

帮助类

公共成员std::hash(std::unique_ptr)(C++11)std::unique_ptr的哈希支持
(函数模板)

示例

#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>

// helper class for runtime polymorphism demo below
struct B
{
virtual ~B() = default;

virtual void bar() {
std::cout
<< "B::bar\n";
}
};

struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }

void bar() override {
std::cout
<< "D::bar\n"; }
};

// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}

// helper function for the custom deleter demo below
void close_file(std::FILE* fp)
{
std::fclose(fp);
}

// unique_ptr-based linked list demo
struct List
{
struct Node
{
int data;
std::unique_ptr<Node> next;
};

std::unique_ptr<Node> head;

~List()
{
// destroy list nodes sequentially in a loop, the default destructor
// would have invoked its `next`'s destructor recursively, which would
// cause stack overflow for sufficiently large lists.
while (head)
head = std::move(head->next);
}

void push(int data)
{
head = std::unique_ptr<Node>(new Node{data, std::move(head)});
}
};

int main()
{
std::cout << "1) Unique ownership semantics demo\n";
{
// Create a (uniquely owned) resource
std::unique_ptr<D> p = std::make_unique<D>();

// Transfer ownership to `pass_through`,
// which in turn transfers ownership back through the return value
std::unique_ptr<D> q = pass_through(std::move(p));

// p is now in a moved-from 'empty' state, equal to nullptr
assert(!p);
}

std::cout << "\n" "2) Runtime polymorphism demo\n";
{
// Create a derived resource and point to it via base type
std::unique_ptr<B> p = std::make_unique<D>();

// Dynamic dispatch works as expected
p->bar();
}

std::cout << "\n" "3) Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // prepare the file to read
{
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
if (fp)
std::cout << char(std::fgetc(fp.get())) << '\n';
} // `close_file()` called here (if `fp` is not null)

std::cout << "\n" "4) Custom lambda-expression deleter and exception safety demo\n";
try
{
std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
});

throw std::runtime_error(""); // `p` would leak here if it were a plain pointer
}
catch (const std::exception&) { std::cout << "Caught exception\n"; }

std::cout << "\n" "5) Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p(new D[3]);
} // `D::~D()` is called 3 times

std::cout << "\n" "6) Linked list demo\n";
{
List wall;
for (int beer = 0; beer != 1'000'000; ++beer)
wall.push(beer);

std::cout << "1'000'000 bottles of beer on the wall...\n";
} // destroys all the beers
}
可能结果
1) Unique ownership semantics demo
D::D
D::bar
D::~D

2) Runtime polymorphism demo
D::D
D::bar
D::~D

3) Custom deleter demo
x

4) Custom lambda-expression deleter and exception safety demo
D::D
destroying from a custom deleter...
D::~D
Caught exception

5) Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D

6) Linked list demo
1'000'000 bottles of beer on the wall...