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 destroyed

  • the 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?

std::unique_ptr<Mesh>::unique_ptr(__gnu_cxx::__alloc_traits<std::allocator<Mesh> >

指向 std::unique_ptr 的内容