龙空技术网

C++(五):如何用unique_ptr管理资源

数据分析的事儿 440

前言:

现在大家对“unique函数怎样不显示错误”大体比较重视,各位老铁们都需要了解一些“unique函数怎样不显示错误”的相关资讯。那么小编也在网上汇集了一些关于“unique函数怎样不显示错误””的相关内容,希望同学们能喜欢,各位老铁们快快来了解一下吧!

为什么需要用智能指针来管理我们的资源呢?

例如,一个二进制文件写入程序

    std::ofstream ofile("sample.bin", std::ios::binary);    if(!ofile.is_open()) {        std::cout << "open file error." << std::endl;        return;    }    std::vector<int> v{1,2,3,4,5};    for(const auto& i : v) {        ofile.write(reinterpret_cast<const char*>(&i), sizeof(i));    }    ofile.close();

我们一般会认为,程序总是会执行到ofile.close()的;但事实上,总会有各种各样的错误或是异常,从而导致程序无法执行到ofile.close();进而导致资源泄漏的发生。更明智的做法,是依托于作用域,来管理资源。

上面这段程序,可以改写成,如下形式:

    auto fclose = [](auto fptr) { fptr->close(); delete fptr;};    auto fptr = new std::ofstream{"sample1.bin", std::ios::binary};    std::unique_ptr<std::ofstream, decltype(fclose)> uptr{fptr, fclose};    if(!uptr->is_open()) {        std::cout << "open file error." << std::endl;        return;    }    std::vector<int> v{1,2,3,4,5};    for(const auto& i : v) {        uptr->write(reinterpret_cast<const char*>(&i), sizeof(i));    }

当然也可以,写成一行代码的形式

std::unique_ptr<std::ofstream, std::function<void(std::ofstream *)>>            uptr(new std::ofstream{"sample.bin", std::ios::binary},                 [](std::ofstream *fptr) {                     fptr->close();                     delete fptr;                 });

读文件

    auto fclose = [](auto fptr) {        fptr->close();        delete fptr;    };    auto fptr = new std::ifstream{"sample1.bin", std::ios::binary};    std::unique_ptr<std::ifstream, decltype(fclose)> uptr{fptr, fclose};    if (!uptr->is_open()) {        std::cout << "open file error." << std::endl;    }    std::vector<int> v;    while (true) {        int i;        uptr->read(reinterpret_cast<char *>(&i), sizeof(i));        if (uptr->fail() || uptr->eof()) break;        v.emplace_back(i);    }    for (const auto &i : v) {        cout << i << endl;    }
std::unique_ptr

std::unique_ptr的构造函数,接受两个参数:

一个数据指针,(这个指针可以指向单一数据,也可以是一个数组指针)一个析构函数,在unique_ptr脱离作用域时调用

unique_ptr(pointer _Ptr, const _Dx& _Dt)

所以,std::unique_ptr的类成员变量是一个类似pair的类,第一个元素为析构函数,第二个元素为数据指针。

_Compressed_pair<_Dx, pointer> _Mypair

我们在构造一个std::unique_ptr实例时,需要显示地指明数据指针类型和析构函数类型,数据指针类型比较容易得到,但析构函数类型如何得到了?

有两种方法:

利用decltype得到析构函数类型利用std::function来显示地指明析构函数类型

std::unique_pt在不显示指明析构函数时,会加载默认的析构函数

struct default_delete { // default deleter for unique_ptr    constexpr default_delete() noexcept = default;    template <class _Ty2, enable_if_t<is_convertible_v<_Ty2*, _Ty*>, int> = 0>    default_delete(const default_delete<_Ty2>&) noexcept {}    void operator()(_Ty* _Ptr) const noexcept /* strengthened */ { // delete a pointer        static_assert(0 < sizeof(_Ty), "can't delete an incomplete type");        delete _Ptr;    }};~unique_ptr() noexcept {        if (_Mypair._Myval2) {            _Mypair._Get_first()(_Mypair._Myval2);        }    }

这个析构函数就做了一件事,调用delete _Ptr来释放内存,它的传入参数是_Ty* _Ptr,由此可得到用std::function表示的析构函数类型

std::function<void(T*)>

结合上面的例子,可以写成

std::unique_ptr<std::ofstream, std::function<void(std::ofstream *)>>            uptr(new std::ofstream{"sample.bin", std::ios::binary},                 [](std::ofstream *fptr) {                     fptr->close();                     delete fptr;                 });

而利用decltype得到析构函数类型比较简单,一路auto过去就行。

    auto fclose = [](auto fptr) { fptr->close(); delete fptr;};    auto fptr = new std::ofstream{"sample1.bin", std::ios::binary};    std::unique_ptr<std::ofstream, decltype(fclose)> uptr{fptr, fclose};

另外在std::unique_ptr中有两个成员函数,比较容易混淆,即

    pointer release() noexcept {        return _STD exchange(_Mypair._Myval2, nullptr);    }    void reset(pointer _Ptr = nullptr) noexcept {        pointer _Old = _STD exchange(_Mypair._Myval2, _Ptr);        if (_Old) {            _Mypair._Get_first()(_Old);        }    }

从上面的源码中,可以看出,release()并不释放内存,仅仅只是将std::unique_ptr中的数据指针置空并抛出数据指针;而reset(pointer _Ptr = nullptr)会调用析构函数,释放内存。

std::make_unique

template <class _Ty, class... _Types, enable_if_t<!is_array_v<_Ty>, int> = 0>_NODISCARD unique_ptr<_Ty> make_unique(_Types&&... _Args) { // make a unique_ptr    return unique_ptr<_Ty>(new _Ty(_STD forward<_Types>(_Args)...));}template <class _Ty, enable_if_t<is_array_v<_Ty> && extent_v<_Ty> == 0, int> = 0>_NODISCARD unique_ptr<_Ty> make_unique(const size_t _Size) { // make a unique_ptr    using _Elem = remove_extent_t<_Ty>;    return unique_ptr<_Ty>(new _Elem[_Size]());}

unique_ptr<_Ty>(new _Ty(_STD forward<_Types>(_Args)...))

从这一行代码中,可以看出,make_unique仅能new出一个数据指针,而无法指定析构函数。所以需要自定义析构函数的时候,不能调用make_unique函数,来产生std::unique_ptr

// auto uptr = std::make_unique<std::ofstream, std::function<void(std::ofstream *)>>//            (new std::ofstream{"sample.bin", std::ios::binary},//                 [](std::ofstream *fptr) {//                    fptr->close();//                    delete fptr;//                }); // error

标签: #unique函数怎样不显示错误