• 智能指针简介


             智能指针用于解决常规指针所带来的内存泄露、重复释放、野指针等内存问题。智能指针基于这样的事实得以发挥作用:定义在栈中的智能指针,当超出其作用域时,会自动调用它的析构函数,从而可以释放其关联的内存资源。

             之前C++标准库中定义的智能指针std::auto_ptr<T>,因其设计存在缺陷,所以已不再推荐使用。C++11引入了新的智能指针:unique_ptr、shared_ptr和weak_ptr。

     

    一:unique_ptr

             unique_ptr类似于auto_ptr。两个unique_ptr实例不能管理同一个对象。

            

    1:类似于auto_ptr,unique_ptr也具有get成员函数,该函数返回指向被管理对象的指针;如果unique_ptr不拥有任何对象,则get返回nullptr;

     

    2:它没有复制构造函数和赋值操作符,而具有移动构造函数和移动赋值操作符;当把源unique_ptr对象移动复制或移动赋值给目标unique_ptr对象后,源unique_ptr对象所掌握的资源的所属权,就转移给目的unique_ptr对象,而源unique_ptr对象就不再掌握资源,所以源unique_ptr对象的unique_ptr::get返回nullptr:

    class X
    {
        int x;
    public:
        X(int x) : x(x) { cout << "ctor invoked: " << x << endl; }
        ~X() { cout << "dtor invoked: " << x << endl; }
        void sayHi() const { cout << "HI: " << x << endl; }
    };
     
    int main()
    {
        unique_ptr<X> a(new X(2));
    
        //unique_ptr<X> b(a);
        //unique_ptr<X> c;  c = a;
    
        unique_ptr<X> d = std::move(a);
        cout << a.get() << endl;
        d->sayHi();
        cout << "---------------
    ";
    
        unique_ptr<X> e(new X(3));
        unique_ptr<X> f;
        f = std::move(e);
        cout << e.get() << endl;
        f->sayHi();
        cout << "---------------
    ";
    }

             unique_ptr没有复制构造函数和复制赋值操作符,只有移动构造函数和移动赋值操作符。所以,在复制或者赋值时,需要使用std::move将对象转换为右值。如果去掉了main函数中的注释代码,则会报编译错误: error: use of deleted function…

    上面代码的运行结果如下:

    ctor invoked: 2
    0
    HI: 2
    ---------------
    ctor invoked: 3
    0
    HI: 3
    ---------------
    dtor invoked: 3
    dtor invoked: 2

             当超出unique_ptr的作用域时,会自动删除它指向的内存对象;

     

             3:unique_ptr的默认行为是掌管指向使用new申请的内存的指针的所属权,并在必要情况下使用delete释放内存。unique_ptr的声明如下:

    template <typename T, typename Deleter = std::default_delete<T>>
    class unique_ptr;

    模板类std::default_delete<T>实现了一个函数对象,调用时执行delete操作,类似于下方的实现:

    template <typename T>
    class default_delete
    {
    public:
        void operator()(T* obj) const
        {
            delete obj;
        }
    };

     

    可以用自己实现的释放资源函数对象,替换默认的deleter。比如,对于使用malloc申请的资源,如果需要用unique_ptr接管的话,必须使用free释放。例子如下:

    struct my_point
    {
        int x; 
        int y;
    };
    
    struct my_point_deleter
    {
        void operator()(my_point* p) const
        {
            cout << "free " << p << endl;
            free(p);
        }
    };
    
    void show_point(const my_point& o)
    {
        cout << "(" << o.x << ", " << o.y << ")" << endl;
    }
    
    int main()
    {
        unique_ptr<my_point, my_point_deleter> p(static_cast<my_point*>(malloc(sizeof(my_point))));
        cout << "p is " << p.get() << endl;
    
        p->x = 5;
        p->y = 8;
        show_point(*p);
    }

    运行结果如下:

    p is 0x2443010
    (5, 8)
    free 0x2443010

     

    4:reset成员函数,用于更改unique_ptr所管理的对象,而原来所掌管的对象则被销毁。移动赋值操作A=B,A原来所掌管的对象也会被销毁:

    int main()
    {
        unique_ptr<X> a(new X(2));
        unique_ptr<X> b(new X(3));
        cout << "---------------
    ";
    
        a = move(b);
        cout << "---------------
    ";
    
        a.reset(new X(4));
        cout << "---------------
    ";
    
        a.reset(nullptr);
        cout << "---------------
    ";
    }

    结果如下:

    ctor invoked: 2
    ctor invoked: 3
    ---------------
    dtor invoked: 2
    ---------------
    ctor invoked: 4
    dtor invoked: 3
    ---------------
    dtor invoked: 4
    ---------------

     

    5:只有非const的unique_ptr能够将其对某对象的所有权转移给其他unique_ptr。也就是说,如果a是个const unique_ptr对象,则下面的语句都是非法的:

        a.reset();
        b = std::move(a);
        unique_ptr<X> c(std::move(a));

     

    6:unique_ptr实现了”*” 和 “->”操作符的重载。因此,可以像使用传统指针那样,使用unique_ptr;

     

    7:unique_ptr实现了operator bool函数。也即是说,可以直接在if()中判断,该unique_ptr是否拥有某个对象:

    int main()
    {
        std::unique_ptr<int> ptr(new int(42));
     
        if (ptr) 
            std::cout << "before reset, ptr is: " << *ptr << '
    ';
            
        ptr.reset();
        
        if (ptr) 
            std::cout << "after reset, ptr is: " << *ptr << '
    ';
        else
            std::cout << "after reset, ptr is null
    ";
    }

    结果如下:

    before reset, ptr is: 42
    after reset, ptr is null

     

    8:C++14引入了make_unique,用于更加方便的在堆上创建对象,并将该对象交给unique_ptr对象掌管。所以,下面两种写法是等价的:

    unique_ptr<my_point> point(new my_point { 6, 5 });
    auto point = make_unique<my_point>(6, 5);

    使用make_unique隐藏了new的调用(that is good because we do not want to have our programs with new but without delete )

     

    9:可以将unique_ptr放到vector中。因为unique_ptr不支持复制,所以在调用push_back时,unique_ptr对象必须是个右值:

    int main()
    {
        std::vector< std::unique_ptr<X> > vu;
    
        vu.push_back(std::unique_ptr<X>(new X(2)));
    
        std::unique_ptr<X> a(new X(3));
        vu.push_back(std::move(a));
    
        for(auto iter = vu.begin(); iter != vu.end(); iter++)
        {
            (*iter)->sayHi();
        }
    }

    结果如下:

    ctor invoked: 2
    ctor invoked: 3
    HI: 2
    HI: 3
    dtor invoked: 2
    dtor invoked: 3

     

    10:unique_ptr也支持管理动态申请的数组:

    class X
    {
        int x;
    public:
        X(int x = 1) : x(x) { cout << "ctor invoked: " << x << endl; }
        ~X() { cout << "dtor invoked: " << x << endl; }
        void sayHi() const { cout << "HI: " << x << endl; }
    };
    
    int main()
    {
        const int size = 3;
        std::unique_ptr<X[]> xs(new X[size]);
        
        for(int i = 0; i < size; i++)
        {
            xs[i].sayHi();
        }
    }

    结果如下:

    ctor invoked: 1
    ctor invoked: 1
    ctor invoked: 1
    HI: 1
    HI: 1
    HI: 1
    dtor invoked: 1
    dtor invoked: 1
    dtor invoked: 1

     

    二:shared_ptr

    shared_ptr使用引用计数,允许多个shared_ptr管理堆中的同一个对象。只有引用计数变为0时,堆中的对象才被销毁。

    多个shared_ptr共享同一对象的所有权,只能是通过复制构造函数或者复制赋值操作符实现。构造shared_ptr时,如果使用的底层指针已经被其他shared_ptr所有,这是一种未定义行为。

     

    1:shared_ptr具有复制构造函数和移动构造函数,可以使用use_count成员函数返回有多少个shared_ptr拥有同一个对象。对于移动构造函数:A(std::move(B)),之后B不再拥有任何对象。

    int main()
    {
        std::shared_ptr<X> sx1(new X(2));
        std::cout << "sx1.get is " << sx1.get() << std::endl;
        std::cout << "sx1.use_count is " << sx1.use_count() << std::endl;
        std::cout << "---------------------
    ";
    
        std::shared_ptr<X> sx2(sx1);
        std::cout << "sx2.get is " << sx2.get() << std::endl;
        std::cout << "sx2.use_count is " << sx2.use_count() << std::endl;
        std::cout << "---------------------
    ";
    
        std::shared_ptr<X> sx3(std::move(sx1));
        std::cout << "sx1.get is " << sx1.get() << std::endl;
        std::cout << "sx1.use_count is " << sx1.use_count() << std::endl;
        std::cout << "sx3.get is " << sx3.get() << std::endl;
        std::cout << "sx3.use_count is " << sx3.use_count() << std::endl;
        std::cout << "---------------------
    ";
    }

    运行结果如下:

    ctor invoked: 2
    sx1.get is 0x12f5010
    sx1.use_count is 1
    ---------------------
    sx2.get is 0x12f5010
    sx2.use_count is 2
    ---------------------
    sx1.get is 0
    sx1.use_count is 0
    sx3.get is 0x12f5010
    sx3.use_count is 2
    ---------------------
    dtor invoked: 2

     

    2:类似于unique_ptr,shared_ptr也支持自定义deleter。

    struct my_point_deleter
    {
        void operator()(my_point* p) const
        {
            cout << "free " << p << endl;
            free(p);
        }
    };
    
    int main()
    {
        std::shared_ptr<my_point> pm(static_cast<my_point*>(malloc(sizeof(my_point))), 
                my_point_deleter());
    
        std::cout << "pm.get is " << pm.get() << std::endl;     
        pm->x = 1;
        pm->y = 2;
    
        show_point(*pm);
        std::cout << "-----------------
    ";
    }

    这里构造shared_ptr对象pm时,第二个参数是my_point_deleter类对象,当需要释放shared_ptr所拥有的对象时,就会自动调用该对象的operator()函数。

    结果如下:

    pm.get is 0xd30010
    (1, 2)
    -----------------
    free 0xd30010

     

    3:reset成员函数,用于更改shared_ptr所管理的对象,而原来所掌管的对象,其引用计数减1:

    如果reset参数为空,则表示shared_ptr释放对源对象的拥有权;

    注意,ptr不能是已经被其他shared_ptr所管理的对象,否则是未定义的;

    int main()
    {
        std::shared_ptr<X> pm1;
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm1.use_count is " << pm1.use_count() << std::endl;
        std::cout << "-----------------
    ";
    
        pm1.reset(new X);
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm1.use_count is " << pm1.use_count() << std::endl;
        pm1->sayHi();
        std::cout << "-----------------
    ";
    
        pm1.reset(new X(2));
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm1.use_count is " << pm1.use_count() << std::endl;
        pm1->sayHi();
        std::cout << "-----------------
    ";
    
        pm1.reset();
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm1.use_count is " << pm1.use_count() << std::endl;
        std::cout << "-----------------
    ";
        return 0;
    }

    结果如下:

    pm1.get is 0
    pm1.use_count is 0
    -----------------
    ctor invoked: 1
    pm1.get is 0xebb010
    pm1.use_count is 1
    HI: 1
    -----------------
    ctor invoked: 2
    dtor invoked: 1
    pm1.get is 0xebb050
    pm1.use_count is 1
    HI: 2
    -----------------
    dtor invoked: 2
    pm1.get is 0
    pm1.use_count is 0
    -----------------

     

    4:复制赋值操作A=B,A原来所掌管的对象的引用计数减1,而B所掌管的对象的引用计数加1,而移动赋值操作符A=B,赋值之后,B不再掌握任何对象:

    int main()
    {
        std::shared_ptr<X> pm1(new X(2));
        std::shared_ptr<X> pm2(new X(3));
        pm2 = pm1;
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm2.get is " << pm2.get() << std::endl;
        std::cout << "pm1.use_count is " << pm1.use_count() << std::endl;
        pm2->sayHi();
        std::cout << "-----------------
    ";
        
        std::shared_ptr<X> pm3;
        pm3 = std::move(pm1);
        std::cout << "pm1.get is " << pm1.get() << std::endl;
        std::cout << "pm2.get is " << pm2.get() << std::endl;
        std::cout << "pm3.get is " << pm3.get() << std::endl;
        std::cout << "pm2.use_count is " << pm2.use_count() << std::endl;
        pm3->sayHi();
        std::cout << "-----------------
    ";
    }

    结果如下:

    ctor invoked: 2
    ctor invoked: 3
    dtor invoked: 3
    pm1.get is 0x968010
    pm2.get is 0x968010
    pm1.use_count is 2
    HI: 2
    -----------------
    pm1.get is 0
    pm2.get is 0x968010
    pm3.get is 0x968010
    pm2.use_count is 2
    HI: 2
    -----------------
    dtor invoked: 2

     

    5:可以将shared_ptr放到容器中。

    int main()
    {
        std::shared_ptr<X> sx1(new X(2));
        std::vector< std::shared_ptr<X> > vs;
    
        vs.push_back(sx1);
        std::cout << "sx1.get is " << sx1.get() << std::endl;
        std::cout << "vs[0].get is " << vs[0].get() << std::endl;
        std::cout << "sx1.use_count is " << sx1.use_count() << std::endl;
        vs[0]->sayHi();
        std::cout << "---------------------
    ";
    
        std::shared_ptr<X> sx2(new X(3));
        std::cout << "sx2.get is " << sx2.get() << std::endl;
    
        vs.push_back(std::move(sx2));
        std::cout << "sx2.get is " << sx2.get() << std::endl;
        std::cout << "vs[1].get is " << vs[1].get() << std::endl;
        std::cout << "vs[1].use_count is " << vs[1].use_count() << std::endl;
        vs[1]->sayHi();
        std::cout << "---------------------
    ";
    }

    结果如下:

    ctor invoked: 2
    sx1.get is 0x1abf010
    vs[0].get is 0x1abf010
    sx1.use_count is 2
    HI: 2
    ---------------------
    ctor invoked: 3
    sx2.get is 0x1abf070
    sx2.get is 0
    vs[1].get is 0x1abf070
    vs[1].use_count is 1
    HI: 3
    ---------------------
    dtor invoked: 3
    dtor invoked: 2

     

    6:shared_ptr实现了operator bool函数。也即是说,可以直接在if()中判断该shared_ptr是否拥有某个对象:

    int main()
    {
        std::unique_ptr<int> ptr(new int(42));
     
        if (ptr) 
            std::cout << "before reset, ptr is: " << *ptr << '
    ';
            
        ptr.reset();
        
        if (ptr) 
            std::cout << "after reset, ptr is: " << *ptr << '
    ';
        else
            std::cout << "after reset, ptr is null
    ";
    }

    结果如下:

    before reset, ptr is: 42
    after reset, ptr is null

     

    7:推荐使用make_shared创建shared_ptr,而不是使用shared_ptr的构造函数。

    一般情况下,shared_ptr内部会持有两个指针,一个是指向被管理对象的指针;另一个是指向控制块的指针。所谓控制块,就是记录引用计数等属性的数据结构。

    当使用make_shared创建shared_ptr时,被管理对象以及控制块的内存是一起申请的;而用构造函数创建的shared_ptr,被管理对象和控制块的内存是分开申请的。所以,make_shared的效率更高一些。

     

    make_shared主要做三件事:

    申请被管理对象以及引用计数的内存;调用适当的构造函数初始化对象;返回一个shared_ptr。

    例子如下:

    int main()
    {
        std::shared_ptr<int> ptrint = std::make_shared<int>(2);
        std::shared_ptr<X> ptrX = std::make_shared<X>(3);
    
        cout << "*ptrint is " << *ptrint << endl;
        ptrX->sayHi();
    }

    结果如下:

    ctor invoked: 3
    *ptrint is 2
    HI: 3
    dtor invoked: 3

     

    三:weak_ptr

    shared_ptr的缺点在于不适用与循环引用的情况,这也是引入std::weak_ptr的原因。所谓循环引用,例子如下:

    struct Child;
     
    struct Parent
    {
        shared_ptr<Child> child;
         
        ~Parent() { cout << "Bye Parent" << endl; }
         
        void hi() const { cout << "Hello" << endl; }
    };
     
    struct Child
    {
        shared_ptr<Parent> parent;
         
        ~Child() { cout << "Bye Child" << endl; }
    };
     
    int main()
    {
        auto parent = make_shared<Parent>();
        auto child = make_shared<Child>();
        parent->child = child;
        child->parent = parent;
        child->parent->hi();
    }

    上面代码的运行结果,只打印出”Hello”,而并没有打印出"Bye Parent"或"Bye Child",说明Parent和Child的析构函数并没有调用到。这是因为Parent和Child对象内部,具有各自指向对方的shared_ptr,加上parent和child这两个shared_ptr,说明每个对象的引用计数都是2。当程序退出时,即使parent和child被销毁,也仅仅是导致引用计数变为了1,因此并未销毁Parent和Child对象。

    这种情况下,就可以使用weak_ptr来解决这种问题:

    struct Child;
     
    struct Parent
    {
        shared_ptr<Child> child;
         
        ~Parent() { cout << "Bye Parent" << endl; }
         
        void hi() const { cout << "Hello" << endl; }
    };
     
    struct Child
    {
        weak_ptr<Parent> parent;
         
        ~Child() { cout << "Bye Child" << endl; }
    };
     
    int main()
    {
        auto parent = make_shared<Parent>();
        auto child = make_shared<Child>();
        parent->child = child;
        child->parent = parent;
        child->parent.lock()->hi();
    }

    上面的代码可以得到正确的结果:

    Hello
    Bye Parent
    Bye Child

     

    1:weak_ptr是对shared_ptr的封装,但是它不拥有被指向的对象,因而也就不会导致引用计数的变化。

    可以使用shared_ptr对象构造weak_ptr,或对其进行赋值。表示该weak_ptr“共享”shared_ptr所拥有的对象。如果shared_ptr为空(不拥有任何对象),则该weak_ptr也是空的。

     

    2:weak_ptr::use_count函数,返回当前有多少个shared_ptr实例共享被管理对象。

    weak_ptr::expired函数检查被管理对象是否已经被销毁了,如果已经被销毁,则该函数返回true,否则返回false。expired函数要比use_count更快一些。例子如下:

    struct Foo {};
     
    int main()
    {
       std::weak_ptr<Foo> w_ptr;
      {
          auto ptr = std::make_shared<Foo>();
          w_ptr = ptr;
          std::cout << "w_ptr.use_count() inside scope: " << w_ptr.use_count() << '
    ';
       }
       std::cout << "w_ptr.use_count() out of scope: " << w_ptr.use_count() << '
    ';
       std::cout << "w_ptr.expired() out of scope: " << std::boolalpha << w_ptr.expired() << '
    ';
    }

    结果如下:

    w_ptr.use_count() inside scope: 1
    w_ptr.use_count() out of scope: 0
    w_ptr.expired() out of scope: true

     

    3:weak_ptr所拥有的,是对shared_ptr所管理对象的弱引用,它必须临时转换为shared_ptr之后,才能临时性的访问引用对象。weak_ptr没有实现->操作符,相反,它实现了lock函数,该函数创建临时的shared_ptr,从而增加了它指向对象的引用计数,避免该对象被释放。

    如果weak_ptr没有引用任何对象,则lock返回的shared_ptr对象也不引用任何对象,否则,lock返回的shared_ptr增加所引用对象的引用计数。

    void observe(std::weak_ptr<int> weak) 
    {
        if (auto observe = weak.lock()) {
            std::cout << "	observe() able to lock weak_ptr<>, value=" << *observe << "
    ";
        } else {
            std::cout << "	observe() unable to lock weak_ptr<>
    ";
        }
    }
     
    int main()
    {
        std::weak_ptr<int> weak;
        std::cout << "weak_ptr<> not yet initialized
    ";
        observe(weak);
     
        {
            auto shared = std::make_shared<int>(42);
            weak = shared;
            std::cout << "weak_ptr<> initialized with shared_ptr.
    ";
            observe(weak);
        }
     
        std::cout << "shared_ptr<> has been destructed due to scope exit.
    ";
        observe(weak);
    }

    上面的代码中,首先构造一个空的weak_ptr对象weak,因此在observe函数中,weak_ptr::lock返回的shared_ptr也是空的;接下来在嵌套作用域中,首先构造了一个指向int类型的shared_ptr,然后用该shared_ptr对weak_ptr进行赋值,这样在observe函数中,weak_ptr::lock返回的shared_ptr也就临时拥有了该int类型对象;嵌套作用域结束后,其中的shared_ptr就被销毁了,weak又再次为空,所以observe函数中,weak_ptr::lock返回的shared_ptr又是空的了。结果如下:

    weak_ptr<> not yet initialized
            observe() unable to lock weak_ptr<>
    weak_ptr<> initialized with shared_ptr.
            observe() able to lock weak_ptr<>, value=42
    shared_ptr<> has been destructed due to scope exit.
            observe() unable to lock weak_ptr<>

     

    四:shared_ptr中的控制结构

    In a typical implementation, std::shared_ptr holds only two pointers:

    the stored pointer (one returned by get());

    a pointer to control block.

     

    The control block is a dynamically-allocated object that holds:

    either a pointer to the managed object or the managed object itself;

    the deleter (type-erased);

    the allocator (type-erased);

    the number of shared_ptrs that own the managed object;

    the number of weak_ptrs that refer to the managed object.

     

             shared_ptr中包含两个指针,一个指向被管理的对象(也就是get()的返回值),另一个指向控制块。

             控制块中包含:指向被管理对象的指针;deleter和allocator;引用计数。比如,shared_ptr<Foo> 的数据结构如下图所示,其中 deleter 和 allocator 是可选的:

     

     

    The pointer held by the shared_ptr directly is the one returned by get(), while the pointer/object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.

    shared_ptr持有的指向被管理对象的指针,就是get()的返回值;而控制块中持有的指针,是当引用计数为0时,会被delete的指针,这两个指针的类型不一定相同,只要它们之间存在隐式转换。

     

    这是 shared_ptr 的一大功能。分 3 点来说:

    1: 无需虚析构

    假设base 是 derived 的基类,但是 base 和 derived 都没有虚析构。

     

    shared_ptr<derived > sp1(new derived );     // 控制块中指针的类型是 derived *

    shared_ptr<base > sp2 = sp1;                         // 可以赋值,自动向上转型(up-cast)

    sp1.reset();                                                         // 这时 derived 对象的引用计数降为 1

    此后 sp2 仍然能安全地管理 derived 对象的生命期,并安全完整地释放 derived ,因为其控制块记住了 derived 的实际类型。具体代码如下:

    class base
    {
    public:
        base() {cout << "base ctor" << endl;}
        ~base() {cout << "base dtor" << endl;}
    };
    
    class derived:public base
    {
    public:
        derived() {cout << "derived ctor" << endl;}
        ~derived() {cout << "derived dtor" << endl;}
    };
    
    int main()
    {
        derived *pd = new derived;
        base *pb = pd;
        delete pb;
    
        cout << "---------------
    ";
    
        shared_ptr<derived> spd(new derived);
        shared_ptr<base> spb = spd;
        spd.reset();
    }

     

             对于原始指针而言,将指向derived的指针pd赋值给指向base的指针pb,在delete pb时,只会调用base的析构函数,而不会调用derived的析构函数。

             而对于shared_ptr而言,使用指向derived的shared_ptr<derived> spd,初始化指向base的shared_ptr<base> spb,当超出作用域时,spb可以完整的释放 derived ,因为其控制块记住了 derived 的实际类型。

             代码结果如下:

    base ctor
    derived ctor
    base dtor
    ---------------
    base ctor
    derived ctor
    derived dtor
    base dtor

     

     

             2:shared_ptr<void> 可以指向并安全地管理(析构或防止析构)任何对象;

    shared_ptr<derived> sp1(new derived);       // 控制块中指针的类型是 derived*

    shared_ptr<void> sp2 = sp1;                                   // 可以赋值,derived* 向 void* 自动转型

    sp1.reset();                                                         // 这时 derived对象的引用计数降为 1

    此后 sp2 仍然能安全地管理 derived对象的生命期,并安全完整地释放 derived,不会出现 delete void* 的情况,因为 delete 的是控制块中的指针,不是 sp2.get()的返回值。

    具体代码如下:

    derived *pd = new derived;
    void *pv = pd;
    delete pv;
    
    cout << "---------------
    ";
    
    shared_ptr<derived> spd(new derived);
    shared_ptr<void> spv = spd;
    spd.reset();

     

    对于原始指针而言,将指向derived的指针pd赋值给void类型的指针pv,在delete pv时,GCC会报编译警告,并且,实际运行时没有任何作用;

    warning: deleting ‘void*’ is undefined [enabled by default]

     

             而对于shared_ptr而言,使用指向derived的shared_ptr<derived> spd,初始化指向void的shared_ptr<void> spv,当超出作用域时,spv可以完整的释放 derived ,因为其控制块记住了 derived 的实际类型。

             代码结果如下:

    base ctor
    derived ctor
    ---------------
    base ctor
    derived ctor
    derived dtor
    base dtor

     

     

    3. 多继承。

    假设 base1 是 derived的多个基类之一,那么:

    shared_ptr<derived> sp1(new derived);

    shared_ptr<base1 > sp2 = sp1;  //这时 sp1.ptr 和 sp2.ptr 可能指向不同的地址,因为

    //base1 subobject 在 derived object 中的 offset 可能不为0。

    sp1.reset();                  // 此时 derived对象的引用计数降为 1

     

    但是 sp2 仍然能安全地管理 derived对象的生命期,并安全完整地释放 derived,因为 delete 的不是 base1 *,而是原来的 derived*。换句话说,sp2中和控制块中的指针可能具有不同的值(当然它们的类型也不同)。

     

    陈硕 http://blog.csdn.net/solstice/article/details/8547547

     

    参考:

    http://en.cppreference.com/w/cpp/memory

    https://oopscenities.net/2013/04/09/smart-pointers-part-2-unique_ptr-2/

  • 相关阅读:
    CentOS重启与关机
    VIM打开文件与保存文件
    sql Split
    JS获取URL参数
    C#后台调用公网接口(GET, POST)
    鼠标右击.exe的程序出现闪退(桌面重启)怎么办
    JS判断有无网络(移动端)
    TFS API : 四、工作项查询
    TFS API:三、TFS WorkItem添加和修改、保存
    TFS API:二、TFS 代码查询工作项
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7492173.html
Copyright © 2020-2023  润新知