美文网首页
C++11智能指针:unique_ptr

C++11智能指针:unique_ptr

作者: 大凡呀 | 来源:发表于2019-04-19 11:27 被阅读0次

    unique_ptr

    1.概念

    unique_ptr形如其名,与所指对象的内存紧密地绑定,不能与其他的unique_ptr类型的指针对象共享所指向对象的内存。

    在cplusplus.com中,unique_ptr声明如下:

    // non-specialized
    template <class T, class D = default_delete<T>> class unique_ptr;
    // array specialization 
    template <class T, class D> class unique_ptr<T[],D>;
    

    是一个模版类,T指得是指向内存的类型,D指得是deleter类型,默认为default_deleter。

    请看如下例子:

    int main(int argc, char* argv[]) {
        std::unique_ptr<int> u1(new int(1));
        std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
        std::unique_ptr<int> u2 = u1;        //编译出错
        return 0;
    }
    

    编译结果如下:


    image

    从编译log来看,use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’,具体原因是unique_ptr不允许与其他对象共享所指向对象的内存,已经删除了拷贝构造函数,无法进行拷贝操作。

    将上述代码改为如下形式编译成功:

    int main(int argc, char* argv[]) {
        std::unique_ptr<int> u1(new int(1));
        std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
        std::unique_ptr<int> u2 = move(u1);
        std::cout << "u2 value : " << *u2 << '\n' << " addredd : " << u2.get() << std::endl;
        return 0;
    }
    

    执行结果如下:


    image

    从结果可以看出u2所指向的值为1,u2所指向的地址与u1所指向的地址相同(通过get成员函数可以获取所指向的地址)。这是由于使用了move操作,u1把内存的所有权释放,u2获取到内存的所有权,所以u2所指向的地址和u1所指向的地址相同。此时如果再次打印u1的值和所指向的内存地址,代码如下:

    int main(int argc, char* argv[]) {
        std::unique_ptr<int> u1(new int(1));
        std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
        std::unique_ptr<int> u2 = move(u1);
        std::cout << "u2 value : " << *u2 << '\n' << " addredd : " << u2.get() << std::endl;
        std::cout << "u1 value : " << *u1 << '\n' << " addredd : " << u1.get() << std::endl;
        return 0;
    }
    

    执行结果如下:


    image

    出现Segmentation fault的原因是u1此时以及把所指内存的所有权转给u2,但还是打印了u1所指的值和地址。在此我就不进行core file分析了。

    2.用法

    1.构造函数

    unique_ptr一共有8个构造函数,分别是:

    描述 函数原型
    1.默认构造函数 constexpr unique_ptr() noexcept;
    2.初始化为空指针 constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {}
    3.初始化为非空指针 explicit unique_ptr (pointer p) noexcept;
    4.初始化为非空指针+左值deleter unique_ptr (pointer p,typename
    5.初始化为非空指针+右值deleter unique_ptr (pointer p,typename remove_reference<D>::type&& del) noexcept;
    6.移动构造函数 unique_ptr (unique_ptr&& x) noexcept;
    7.非默认deleter的移动构造 template <class U, class E> unique_ptr (unique_ptr<U,E>&& x) noexcept;
    8.从auto_ptr类型移动(C++17中移除掉这种用法了) template <class U> unique_ptr (auto_ptr<U>&& x) noexcept;
    9.拷贝构造函数(已删除,不可使用 unique_ptr (const unique_ptr&) = delete;

    下面我们分别测试一下每种构造函数的用法:

    #include <memory>
    #include <iostream>
    
    using namespace std;
    
    class smart_point_class {
    public:
        smart_point_class(int i) : _i(i) {
            cout << "Default construct..." << _i << endl;
        }
        ~smart_point_class() {
            cout << "Destroct..." << _i << endl;
        }
        inline int get_value() {
            return _i;
        }
    
    private:
        smart_point_class(const smart_point_class&) = delete;
        smart_point_class(smart_point_class&&) = delete;
    
    private:
        int _i;
    };
    
    class deleter {
    public:
        deleter() = default;
        deleter(const deleter& other) {
            cout << "Copy deleter." << endl;
        }
        deleter(deleter&& other) {
            cout << "Move deleter." << endl;
        }
        deleter& operator=(const deleter&) {
            cout << "Copy assign deleter." << endl;
            return *this;
        }
        deleter& operator=(deleter&&) {
            cout << "Move assign deleter." << endl;
            return *this;
        }
    
        void operator() (smart_point_class* spc) {
            cout << "Start of deleter function : " << spc->get_value() << endl;
            delete spc;
            spc = nullptr;
            cout << "End of deleter function." << endl;
        }
    };
    
    void test_construct_fun() {
        cout << "C++ unique_ptr test function :" << endl;
        cout << "----------------------------------------------------" << endl;
        cout << "u1" << endl;
        unique_ptr<smart_point_class> u1;
        cout << "u1 : " << (u1? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u2" << endl;
        unique_ptr<smart_point_class> u2(nullptr);
        cout << "u2 : " << (u2? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u3" << endl;
        unique_ptr<smart_point_class> u3(new smart_point_class(3));
        cout << "u3 : " << (u3? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u4" << endl;
        deleter d;
        unique_ptr<smart_point_class, decltype(d)> u4(new smart_point_class(4), d);
        cout << "u4 : " << (u4? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u5" << endl;
        unique_ptr<smart_point_class, deleter> u5(new smart_point_class(5), deleter());
        cout << "u5 : " << (u5? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u6" << endl;
        unique_ptr<smart_point_class> u6 = move(u3);
        cout << "u6 : " << (u6? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u7" << endl;
        unique_ptr<smart_point_class, deleter> u7 = move(u5);
        cout << "u7 : " << (u7? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
        cout << "u8" << endl;
        unique_ptr<smart_point_class> u8 (auto_ptr<smart_point_class>(new smart_point_class(8)));
        cout << "u8 : " << (u8? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
    
        cout << "Test finish..." << endl;
        cout << "u1 : " << (u1? "not null" : "null") << '\n';
        cout << "u2 : " << (u2? "not null" : "null") << '\n';
        cout << "u3 : " << (u3? "not null" : "null") << '\n';
        cout << "u4 : " << (u4? "not null" : "null") << '\n';
        cout << "u5 : " << (u5? "not null" : "null") << '\n';
        cout << "u6 : " << (u6? "not null" : "null") << '\n';
        cout << "u7 : " << (u7? "not null" : "null") << '\n';
        cout << "u8 : " << (u8? "not null" : "null") << '\n';
        cout << "----------------------------------------------------" << endl;
    }
    
    int main(int argc, char* argv[]) {
        test_construct_fun();
        return 0;
    }
    

    编译结果如下:


    image

    会发现有个警告,是因为在C++11中不再建议使用auto_ptr了,但既然构造函数中有这个用法,我们就在这里测试一下(在C++17中已经移除掉这种用法了)。

    这段代码执行结果如下:


    image

    现在我们来分析一下执行结果:

    1. u1使用了默认构造函数,所指向内容为空;
    2. u2使用了初始化为空指针的构造函数,所指向内容为空;
    3. u3使用了初始化为非空指针的构造函数,所指向内容不为空;
    4. u4使用了初始化为非空指针+左值deleter的构造函数,会线构造一个deleter,然后拷贝这个deleter,所指向内容不为空;
    5. u5使用了初始化为非空指针+右值deleter的构造函数,会线构造一个deleter,然后移动这个deleter,所指向内容不为空;
    6. u6使用了移动构造函数,u3将所有权转到了u6,u6不为空,但u3变为空;
    7. u7使用了自定义deleter类型的移动构造函数,u5将所有权转到了u7,u7不为空,但u5变为空;
    8. u8使用了从auto_ptr类型移动的构造函数,不为空,但C++17已经移除了这种用法,不再建议使用;
    9. 在函数体内所有语句执行完毕之后,释放资源。其中使用默认deleter的会调用smart_pointer_class的析构函数,不使用默认deleter的会调用deleter函数/仿函数。

    2.析构函数

    没有需要特殊说明的。

    3.赋值重载操作

    unique_ptr一共有3个赋值重载操作,分别是:

    描述 函数原型
    1.移动赋值 unique_ptr& operator= (unique_ptr&& x) noexcept;
    2.赋值为空 unique_ptr& operator= (nullptr_t) noexcept;
    3.赋值非默认deleter的unique_ptr template <class U, class E> unique_ptr& operator= (unique_ptr<U,E>&& x) noexcept;
    4.拷贝赋值函数(已删除,不可使用) unique_ptr& operator= (const unique_ptr&) = delete;

    下面我们分别测试一下每种赋值函数的用法:

    #include <memory>
    #include <iostream>
    
    using namespace std;
    
    class smart_point_class {
    public:
        smart_point_class(int i) : _i(i) {
            cout << "Default construct..." << _i << endl;
        }
        ~smart_point_class() {
            cout << "Destroct..." << _i << endl;
        }
        inline int get_value() {
            return _i;
        }
    
    private:
        smart_point_class(const smart_point_class&) = delete;
        smart_point_class(smart_point_class&&) = delete;
    
    private:
        int _i;
    };
    
    class deleter {
    public:
        deleter() = default;
        deleter(const deleter& other) {
            cout << "Copy deleter." << endl;
        }
        deleter(deleter&& other) {
            cout << "Move deleter." << endl;
        }
        deleter& operator=(const deleter&) {
            cout << "Copy assign deleter." << endl;
            return *this;
        }
        deleter& operator=(deleter&&) {
            cout << "Move assign deleter." << endl;
            return *this;
        }
    
        void operator() (smart_point_class* spc) {
            cout << "Start of deleter function : " << spc->get_value() << endl;
            delete spc;
            spc = nullptr;
            cout << "End of deleter function." << endl;
        }
    };
    
    void test_assign_fun() {
        cout << "C++ unique_ptr assign test :" << endl;
        cout << "u_base" << endl;
        unique_ptr<smart_point_class> u_base(new smart_point_class(1));
        cout << "u_base value : " << u_base->get_value() << '\n' << " addredd : " << u_base.get() << endl;
        cout << "----------------------------------------------------" << endl;
        cout << "u1" << endl;
        unique_ptr<smart_point_class> u1;
        u1 = move(u_base);
        cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
        cout << "----------------------------------------------------" << endl;
        unique_ptr<smart_point_class> u2(new smart_point_class(2));
        u2 = nullptr;
        cout << "----------------------------------------------------" << endl;
        unique_ptr<smart_point_class, deleter> u4;
        u4 = unique_ptr<smart_point_class, deleter>(new smart_point_class(3));
        cout << "----------------------------------------------------" << endl;
        cout << "Test finish..." << endl;
    }
    
    int main(int argc, char* argv[]) {
        test_assign_fun();
        return 0;
    }
    

    上述代码执行结果如下:


    image

    现在我们来分析一下执行结果:

    1. u1从u_base处获取所有权;
    2. u2使用了初始化为非空指针的构造函数,所指向内容不为空,然后赋值为空,u2 = nullptr,等同于u2.reset(),(在后面会具体说到reset的使用方法),在此进行析构操作;
    3. u3首先指向空,然后用右值赋值,调用了unique_ptr的移动赋值函数;
    4. 所有语句执行完成之后,释放资源。u3会调用deleter函数/仿函数,u2在之前已经释放,最后释放u1,用默认的deleter。

    4.get()

    get函数会返回存储的指针。如果由unique_ptr不为空,则存储的指针指向由unique_ptr管理的对象,否则指向nullptr。需要注意的是调用这个函数并不会释放指针的所有权(它仍然负责删除管理的数据)。因此,此函数的返回值不用于构建新的指针。如果要获取存储的指针并释放所有权,调用unique_ptr::release()。

    get函数使用方法如下:

    int main(int argc, char* argv[]) {
        int *i = new int(1);
        cout << "i : " << i << endl;
        unique_ptr<int> u(i);
        cout << "u value : " << *u << '\n' << "u address : " << u.get() << endl;
        return 0;
    }
    

    该段代码执行结果如下:


    image

    从图中看出可以正确打印出地址。

    5.get_deleter()

    get_deleter函数会返回deleter。deleter是一个可调用的对象。使用成员类型指针的单个参数对该对象的函数调用将删除托管对象,并在unique_ptr本身被销毁、分配了一个新值或在非空时重置时自动调用。unique_ptr模板使用的默认deleter类型是default_delete,一个无状态类。

    get_deleter使用例子:

    int main(int agrc, char* argv[]) {
        // smart_point_class和deleter在之前的代码中有定义
        unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
        smart_point_class* p(new smart_point_class(2));
    
        cout << "----------------------------------------------------" << endl;
        u.get_deleter()(p);
        cout << "----------------------------------------------------" << endl;
    }
    

    上述代码执行结果如下:


    image

    在两条分割线之间的语句u.get_deleter()(p),就等同于调用deleter函数/仿函数。

    6.bool操作重载

    没有需要特殊说明的。

    7.release()

    该操作会释放指针的所有权,并通过返回值返回。此调用不会销毁托管对象,但unique_ptr对象将从删除对象的责任中释放。某些其他实体必须负责删除对象。

    请看如下代码:

    int main(int agrc, char* argv[]) {
        unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
        u.release();
    }
    

    上述代码执行结果如下:


    image

    会发现并没有对u存储的指针进行销毁的操作,这样非常容易造成内存泄漏。代码应该改为如下形式:

    int main(int agrc, char* argv[]) {
        unique_ptr<smart_point_class, deleter> u(new smart_point_class(1), deleter());
        smart_point_class *p = u.release();
        delete p;
    }
    

    上述代码执行结果如下:


    image

    8.reset()

    销毁当前由unique_ptr管理的对象(如果有的话),并获取p的所有权。如果p是一个空指针(例如默认初始化的指针),unique_ptr将变为空,在调用之后不管理任何对象。若要释放存储指针的所有权而不破坏它,请使用成员函数release。

    release使用例子:

    int main(int agrc, char* argv[]) {
        unique_ptr<smart_point_class> u1(new smart_point_class(1));
        cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
        smart_point_class *p (new smart_point_class(2));
        cout << "----------------------------------------------------" << endl;
        u1.reset(p);
        cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
        cout << "----------------------------------------------------" << endl;
        u1.reset();
        cout << "----------------------------------------------------" << endl;
    }
    

    上述代码执行结果如下:


    image
    1. 在执行完u1.reset(p)语句之后,会首先销毁资源,然后u1所管理内存的value由1变为2,对应的地址也有了改变;
    2. 在执行完u1.reset()语句之后,会销毁u1现在所管理内存的资源。

    9.swap()

    交换两个unique_ptr所管理的对象。

    swap使用例子:

    int main(int agrc, char* argv[]) {
        unique_ptr<smart_point_class> u1(new smart_point_class(1));
        unique_ptr<smart_point_class> u2(new smart_point_class(2));
        cout << "----------------------------------------------------" << endl;
        cout << "Before swap :" << endl;
        cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
        cout << "u2 value : " << u2->get_value() << '\n' << " addredd : " << u2.get() << endl;
        cout << "----------------------------------------------------" << endl;
        u1.swap(u2);
        cout << "After swap :" << endl;
        cout << "u1 value : " << u1->get_value() << '\n' << " addredd : " << u1.get() << endl;
        cout << "u2 value : " << u2->get_value() << '\n' << " addredd : " << u2.get() << endl;
        cout << "----------------------------------------------------" << endl;
    }
    

    上述代码执行结果如下:


    image

    可以看到u1和u2所管理的对象做了交换操作。

    10. * ->操作

    与普通指针操作相同,在此不再赘述。

    相关文章

      网友评论

          本文标题:C++11智能指针:unique_ptr

          本文链接:https://www.haomeiwen.com/subject/uhtfgqtx.html