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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的堆内存⽆法同其它 unique_ptr 共享,也就是说,每个 unique_ptr 指针都独⾃拥有对其所指堆内存空间的所有 权。 这也就意味着,每个 unique_ptr 指针指向的堆内存空间的引⽤计数,都只能为 1,⼀旦该 unique_ptr 指针放弃对所指堆内存空 间的所有权,则该空间会被⽴即释放回收。 unique_ptr 智能指针是以模板类的形式提供的,unique_ptr<T>(T 为指针所指数据的类型)定义在<memory>头⽂件,并位于 std 命名空间 中。因此,要想使⽤ unique_ptr 类型指针,程序中应⾸先包含如下 2 条语句: 1. #include <memory> 2. using namespace std; 第 2 句并不是必须的,可以不添加,则后续在使⽤ unique_ptr 指针时,必须标注std:: 。 unique_ptr智能指针的创建 智能指针的创建 考虑到不同实际场景的需要,unique_ptr<T> 模板类提供了多个实⽤的构造函数,这⾥给读者列举了⼏种常⽤的构造 unique_ptr 智能指针的⽅式。 1) 通过以下 2 种⽅式,可以创建出空的 unique_ptr 指针: 1. std::unique_ptr<int> p1(); 2. std::unique_ptr<int> p2(nullptr); 2) 创建 unique_ptr 指针的同时,也可以明确其指向。例如: 1. std::unique_ptr<int> p3(new int); 由此就创建出了⼀个 p3 智能指针,其指向的是可容纳 1 个整数的堆存储空间。 和可以⽤ make_shared<T>() 模板函数初始化 shared_ptr 指针不同,C++11 标准中并没有为 unique_ptr 类型指针添 加类似的模板函数。 3) 基于 unique_ptr 类型指针不共享各⾃拥有的堆内存,因此 C++11 标准中的 unique_ptr 模板类没有提供拷贝构造函数,只提供 了移动构造函数。例如: 1. std::unique_ptr<int> p4(new int); 2. std::unique_ptr<int> p5(p4);//错误,堆内存不共享 3. std::unique_ptr<int> p5(std::move(p4));//正确,调⽤移动构造函数 值得⼀提的是,对于调⽤移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,⽽ p4 将变成空指针(nullptr)。 4) 默认情况下,unique_ptr 指针采⽤ std::default_delete<T> ⽅法释放堆内存。当然,我们也可以⾃定义符合实际场景的释放规 则。值得⼀提的是,和 shared_ptr 指针不同,为 unique_ptr ⾃定义释放规则,只能采⽤函数对象的⽅式。例如: 1. //⾃定义的释放规则 2. struct myDel 3. { 4. void operator()(int *p) { 5. delete p; 6. } 7. }; 8. std::unique_ptr<int, myDel> p6(new int); 9. //std::unique_ptr<int, myDel> p6(new int, myDel()); unique_ptr<T>模板类提供的成员⽅法 模板类提供的成员⽅法 为了⽅便⽤户使⽤ unique_ptr 智能指针,unique_ptr<T> 模板类还提供有⼀些实⽤的成员⽅法,它们各⾃的功能如表 1 所⽰。 表 1 unique_ptr指针可调⽤的成员函数 成员函数名 成员函数名 功 功 能 能 operator*() 获取当前 unique_ptr 指针指向的数据。 operator->() 重载 -> 号,当智能指针指向的数据类型为⾃定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 operator =() 重载了 = 赋值号,从⽽可以将 nullptr 或者⼀个右值 unique_ptr 指针直接赋值给当前同类型的 unique_ptr 指针。 operator []() 重载了 [] 运算符,当 unique_ptr 指针指向⼀个数组时,可以直接通过 [] 获取指定下标位置处的数据。 get() 获取当前 unique_ptr 指针内部包含的普通指针。 get_deleter()

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值