C++智能指针之unique_ptr

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.jianshu.com/p/b8d6b10da667

智能指针

1.什么是智能指针

最近有个段子

  • C语言:搬石头砸自己的脚;
  • C++:搬石头砸自己的脚,也可能砸别人的脚;
  • python:点个按钮,自动搬石头;

在三大常用语言中,C/C++, java,python中,通常情况下C/C++性能最好,但是大部分开发这都喜欢java和python,其中主要的原因之一是C/C++缺少智能内存回收,在复杂的系统中,经常遇到一个常见的问题 -- 内存泄露

对于一个程序员,码农最头大的事情就是内存泄露。君不见,内存泄露吼三吼。

C++的开发这都会想,有没有一种方式能像java和python一样方便,系统自动释放呢?
unique_ptr 是C++ 11 提供的用于防止内存泄漏的智能指针中的一种实现,独享被管理对象指针所有权的智能指针(shared_ptr 下次在分享)。

2.智能指针的实现

unique_ptr定义头文件中

template > class unique_ptr;
template  class unique_ptr;

std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。

在下列两者之一发生时用关联的删除器释放对象:

  • 销毁了管理的 unique_ptr 对象
  • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。

通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。

unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)

std::unique_ptr 有两个版本:

  1. 管理个对象(例如以 new 分配)

  2. 管理动态分配的对象数组(例如以 new[] 分配)

类满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 的要求。

unique_ptr的使用

备注:make_unique 是C++14的新特性,如果使用C++11编译,请把make_unique改成std::unique_ptr的形式。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct B {
    virtual void bar() { std::cout << "B::bar\n"; }
    virtual ~B() = default;
};
struct D : B
{
    D() { std::cout << "D::D\n";  }
    ~D() { std::cout << "D::~D\n";  }
    void bar() override { std::cout << "D::bar\n";  }
};

// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr pass_through(std::unique_ptr p)
{
    p->bar();
    return p;
}

void close_file(std::FILE* fp) { std::fclose(fp); }

int main()
{
    std::cout << "unique ownership semantics demo\n";
    {
        auto p = std::make_unique(); // p 是占有 D 的 unique_ptr
        auto q = pass_through(std::move(p));
        assert(!p); // 现在 p 不占有任何内容并保有空指针
        q->bar();   // 而 q 占有 D 对象
    } // ~D 调用于此

    std::cout << "Runtime polymorphism demo\n";
    {
        std::unique_ptr p = std::make_unique(); // p 是占有 D 的 unique_ptr
        // 作为指向基类的指针
        p->bar(); // 虚派发

        std::vector> v;  // unique_ptr 能存储于容器
        v.push_back(std::make_unique());
        v.push_back(std::move(p));
        v.emplace_back(new D);
        for(auto& p: v) p->bar(); // 虚派发
    } // ~D called 3 times

    std::cout << "Custom deleter demo\n";
    std::ofstream("demo.txt") << 'x'; // 准备要读的文件
    {
        std::unique_ptr fp(std::fopen("demo.txt", "r"),
                                                             close_file);
        if(fp) // fopen 可以打开失败;该情况下 fp 保有空指针
            std::cout << (char)std::fgetc(fp.get()) << '\n';
    } // fclose() 调用于此,但仅若 FILE* 不是空指针
    // (即 fopen 成功)

    std::cout << "Custom lambda-expression deleter demo\n";
    {
        std::unique_ptr> p(new D, [](D* ptr)
        {
            std::cout << "destroying from a custom deleter...\n";
            delete ptr;
        });  // p 占有 D
        p->bar();
    } // 调用上述 lambda 并销毁 D

    std::cout << "Array form of unique_ptr demo\n";
    {
        std::unique_ptr p{new D[3]};
    } // 调用 ~D 3 次
}

运行结果:

unique ownership semantics demo
D::D
D::bar
D::bar
D::~D
Runtime polymorphism demo
D::D
D::bar
D::D
D::D
D::bar
D::bar
D::bar
D::~D
D::~D
D::~D
Custom deleter demo
x
Custom lambda-expression deleter demo
D::D
D::bar
destroying from a custom deleter...
D::~D
Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D

智能指针使用总结:

  1. 智能指针自己管理内存的声明周期;
  2. 智能指针在构造是可以制定对应了销毁函数;

进阶用法:

在上述的智能指针使用中,我们通过传入delete的函数,这里可以采用更优雅的方式:通过结构体的运算符重载达到delete函数的效果

#include 
#include 
using namespace std;

typedef struct _package{
    unsigned char* data;
    int length;
}package_t;

void release_package(package_t* package){
    cout<<"release_package"<data){
        delete[] package->data;
    }
    delete package;
}

struct package_destuctor{
    void operator()(package_t* package){
        release_package(package);
    }
};


// unique_ptr 不能拷贝或者复制
int main(){
    unique_ptr ret1(new package_t(), release_package);
    unique_ptr ret(new package_t());
    return 0;
}

运行结果:
两种方式都正常release数据

release_package
release_package

进阶二

unique_ptr作为形参时,必须保证不能发生COPY
例如:

unique_ptr uptr(new T);
show_ptr(uptr);

这种调用会出错,原因在于uptr作为形参时,会发生copy,而unique_ptr不允许Copy。

#include 
#include 

using namespace std;

class MyTest{
public:
    MyTest(const string & name)
        :_name(name){
        cout<<"MyTest:"<<_name<_name=another._name;
        cout< retDying(string param){
    return unique_ptr(new MyTest(param));
}

//②返回一个局部对象;
unique_ptr retTemp(string param){
    unique_ptr pTemp(new MyTest(param));
    return pTemp;
}

//unique_ptr可以作为形参,必须保证不能发生copy
unique_ptr show(unique_ptr up){
    cout<<*up<& ptr){
    if(!ptr){
        cout<<"delete_ptr ptr is null"< ret1 = retDying("dying");
    cout<<(*ret1)._name< pCount(new int(10));
    //unique_ptr可以作为形参,必须保证不能发生copy,pCount不能当做参数,可以使用转移或者move
    show(unique_ptr(new int(10)));
    show(move(pCount));

    unique_ptr ret2 = retTemp("temp");
    cout<<(*ret2)._name< retp(ret1.release())
    release(ret1);
    return 0;
}

结果如下:

MyTest:dying
dying
10
10
MyTest:temp
temp
~MyTest:temp
~MyTest:dying

unique_ptr之release重点声明

unique_ptr中的release方法,第一眼看过去,是释放内存,其实并不是,并不是,并不是,重要的事情说三遍
看unique_ptr的release函数声明:

pointer release() noexcept;

其功能是当前的智能指针释放对持有指针的控制,并返回持有的指针,其含义是:release之后,大爷不管了,返回的指针你自己玩吧, 千万不要想当然的认为是释放内存,只是释放控制权!!!

参考:

https://zh.cppreference.com/w/cpp/memory/unique_ptr/

你可能感兴趣的:(C++智能指针之unique_ptr)