前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】智能指针:unique_ptr

【C++】智能指针:unique_ptr

作者头像
灰子学技术
发布2020-12-08 17:01:47
7840
发布2020-12-08 17:01:47
举报
文章被收录于专栏:灰子学技术灰子学技术

一、产生的原因:

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;

代码语言:javascript
复制
#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;

代码语言:javascript
复制
#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;

代码语言:javascript
复制
#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;

代码语言:javascript
复制
#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;

代码语言:javascript
复制
// 例子只是用来展示对于数组的操作方法
#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的主要源码如下所示:

代码语言:javascript
复制
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

补充资料:

http://senlinzhan.github.io/2015/04/20/%E8%B0%88%E8%B0%88C-%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/ https://en.cppreference.com/w/cpp/memory/unique_ptr https://www.cnblogs.com/DswCnblog/p/5628195.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 灰子学技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档