std::unique_ptr
Posted 清水寺扫地僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了std::unique_ptr相关的知识,希望对你有一定的参考价值。
一、产生的原因:
unique_ptr的产生,就是为了解决,raw pointer 的new和delete配对使用问题。对于raw pointer来说,在new了之后,在delete之前往往会出现程序异常,进而导致delete没有被释放,如此以来就会产生内存泄漏。引入了unique_ptr之后,可以有效的减轻C++程序员对于raw pointer的使用负担。参考官方文档:
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.
https://en.cppreference.com/w/cpp/memory/unique_ptr
二、特性:
也正是因为上面的原因,unique_ptr具有两个特性:
特性1: 替代raw pointer,来封装对象,进行操作,不需要考虑内存泄漏,参考官方文档。
The object is disposed of, using the associated deleter when either of the following happens:
the managing
unique_ptr
object is destroyedthe managing
unique_ptr
object is assigned another pointer via operator= or reset().https://en.cppreference.com/w/cpp/memory/unique_ptr
特性2: 具有ownership transfer的能力, 参考官方文档。
Only non-const unique_ptr
can transfer the ownership of the managed object to another unique_ptr
. If an object's lifetime is managed by a const std::unique_ptr, it is limited to the scope in which the pointer was created.
std::unique_ptr
is commonly used to manage the lifetime of objects, including:
-
providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception
-
passing ownership of uniquely-owned objects with dynamic lifetime into functions
-
acquiring ownership of uniquely-owned objects with dynamic lifetime from functions
-
as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)
三、常用操作例子:
operate = :参数是右值
unique_ptr& operator=( unique_ptr&& r ) noexcept;
#include <iostream>
#include <memory>
struct Foo {
int id;
Foo(int id) : id(id) { std::cout << "Foo " << id << '\\n'; }
~Foo() { std::cout << "~Foo " << id << '\\n'; }
};
int main()
{
std::unique_ptr<Foo> p1( std::make_unique<Foo>(1) );
{
std::cout << "Creating new Foo...\\n";
std::unique_ptr<Foo> p2( std::make_unique<Foo>(2) );
// p1 = p2; // Error ! can't copy unique_ptr
p1 = std::move(p2);
std::cout << "About to leave inner block...\\n";
// Foo instance will continue to live,
// despite p2 going out of scope
}
std::cout << "About to leave program...\\n";
}
/***********************************************************/
输出:
Foo 1 // 构造函数创建Foo 1
Creating new Foo... // 进入{ scope
Foo 2 // 构造函数创建 Foo 2
~Foo 1 // std:move进行ownership tansfer,此时Foo 1被析构掉, 并将Foo 2的值给Foo 1
About to leave inner block...
About to leave program...
~Foo 2 // main函数退出,将剩下的 Foo 2 析构掉
pointer release() noexcept;
#include <memory>
#include <iostream>
#include <cassert>
struct Foo {
Foo() { std::cout << "Foo\\n"; }
~Foo() { std::cout << "~Foo\\n"; }
void Print() {std::cout<<"print"<<std::endl;}
};
int main()
{
std::cout << "Creating new Foo...\\n";
std::unique_ptr<Foo> up(new Foo());
std::cout << "About to release Foo...\\n";
Foo* fp = up.release(); // 释放unique_ptr,并将raw pointer返回
assert (up.get() == nullptr);
assert (up == nullptr);
std::cout << "Foo is no longer owned by unique_ptr...\\n";
fp->Print();
delete fp;
}
/***********************************************************/
输出:
Creating new Foo...
Foo // unique_str调用构造函数创建指针
About to release Foo...
Foo is no longer owned by unique_ptr...
print // release返回的raw pointer 可以继续使用
~Foo // main函数退出,调用构造
void reset( pointer ptr = pointer() ) noexcept;
#include <iostream>
#include <memory>
struct Foo { // object to manage
Foo() { std::cout << "Foo...\\n"; }
~Foo() { std::cout << "~Foo...\\n"; }
};
struct D { // deleter
void operator() (Foo* p) {
std::cout << "Calling delete for Foo object... \\n";
delete p;
}
};
int main()
{
std::cout << "Creating new Foo...\\n";
std::unique_ptr<Foo, D> up(new Foo(), D()); // up owns the Foo pointer (deleter D)
std::cout << "Replace owned Foo with a new Foo...\\n";
up.reset(new Foo()); // calls deleter for the old one
std::cout << "Release and delete the owned Foo...\\n";
up.reset(nullptr);
}
/***********************************************************/
输出:
Creating new Foo... // 创建Foo,指定删除函数
Foo...
Replace owned Foo with a new Foo...
Foo... // reset之后 生成新的的Foo
Calling delete for Foo object... // 指定的删除函数D
~Foo... // 调用旧Foo的析构函数
Release and delete the owned Foo...
Calling delete for Foo object... // 用nullptr来清空新的Foo
~Foo...
void swap(unique_ptr& other) noexcept;
#include <iostream>
#include <memory>
struct Foo {
Foo(int _val) : val(_val) { std::cout << "Foo...\\n"; }
~Foo() { std::cout << "~Foo...\\n"; }
int val;
};
int main()
{
std::unique_ptr<Foo> up1(new Foo(1));
std::unique_ptr<Foo> up2(new Foo(2));
up1.swap(up2);
std::cout << "up1->val:" << up1->val << std::endl;
std::cout << "up2->val:" << up2->val << std::endl;
}
/***********************************************************/
输出:
Foo...
Foo...
up1->val:2 // swap之后交换对象
up2->val:1
~Foo...
~Foo...
T& operator[]( std::size_t i ) const;
// 例子只是用来展示对于数组的操作方法
#include <iostream>
#include <memory>
int main()
{
const int size = 10;
std::unique_ptr<int[]> fact(new int[size]);// 操作数组的格式
for (int i = 0; i < size; ++i) {
fact[i] = (i == 0) ? 1 : i * fact[i-1];
}
for (int i = 0; i < size; ++i) {
std::cout << i << "! = " << fact[i] << '\\n';
}
}
/***********************************************************/
输出:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
四、主要函数实现:
unique_ptr的主要源码如下所示:
namespace std {
template <typename T, typename D = default_delete<T>>
class unique_ptr
{
public:
explicit unique_ptr(pointer p) noexcept;
~unique_ptr() noexcept;
T& operator*() const;
T* operator->() const noexcept;
unique_ptr(const unique_ptr &) = delete;
unique_ptr& operator=(const unique_ptr &) = delete;
unique_ptr(unique_ptr &&) noexcept;
unique_ptr& operator=(unique_ptr &&) noexcept;
// ...
private:
pointer __ptr;
};
}
1. 内部存储一个 raw pointer,当unique_ptr
析构时,它的析构函数将会负责析构它持有的对象。
2.提供了operator*()
和operator->()
成员函数,像 raw pointer 一样,我们可以使用*
解引用unique_ptr
,使用->
来访问unique_ptr
所持有对象的成员。
3.并不提供 copy 操作(这里指的是copy构造和赋值构造),这是为了防止多个unique_ptr
指向同一对象。但却有一个例外:可以从函数中返回一个unique_ptr。
4.提供了 move 操作,因此我们可以用std::move()
来转移unique_ptr
,也即是转移构造函数和转移赋值函数,涉及到右值引用的概念,
相关内容可见:右值引用&&。
补充资料:
深入 C++ 的 unique_ptr;
std::unique_ptr;
C++ 11创建和使用 unique_ptr;
以上是关于std::unique_ptr的主要内容,如果未能解决你的问题,请参考以下文章
错误:使用已删除的函数‘std::unique_ptr<...> [关闭]
使用 std::unique_ptr 的 C++ Pimpl Idiom 不完整类型
将 std::unique_ptr 传递给 CListBox::GetSelItems
std::unique_ptr<Mesh>::unique_ptr(__gnu_cxx::__alloc_traits<std::allocator<Mesh> >