C++11新特性之智能指针

  1、shared_ptr:一种计数指针,被指向的对象在引用计数为0时删除。它表示共享的所有权(负责对象的删除销毁)。需要包含<memory>,下同。

// 定义删除器
struct Deleter
{
public:
    void operator() (Base *p)
    {
        cout << "[deleter called]" << endl;
        delete p;
    }
};

int main()
{
    shared_ptr<Base> p1(new Base, Deleter());
    {
        shared_ptr<Base> p2(p1);    // 这里p1/p2的use_count()为2
        {
            shared_ptr<Base> p3(p2);    // 这里p1/p2/p3的use_count()为3
        }    // p1/p2的use_count()变为2
    }    // p1的use_count()变为1

    p1.reset();    // 无参的reset()将使p1变为空(就像默认构造时一样)

    // p1的use_count()变为0且上面的new Base被删除/析构

    return 0;
}

  以下用法是错误的:

// 错误1:既使用shared_ptr,又自己管理原生指针。原生指针被重复析构
int *p = new int(9);
shared_ptr<int> sp1(p);
delete p;

// 错误2:多个shared_ptr均从同一个原生指针构造。原生指针被重复析构
// 多个shared_ptr管理同一个原生指针,则后续的shared_ptr都应直接或间接(通过weak_ptr)从第一个构造
int *q = new int(10);
shared_ptr<int> sp2(q);
shared_ptr<int> sp3(q);

  有时候,需要在类的成员函数里面获取到“指向”当前对象的shared_ptr(然后把它传递给其他函数)。此时,只需要继承enable_shared_from_this,即可在成员函数中使用shared_from_this()获取到shared_ptr。示例:

class Foo;

class Bar
{
public:
    void test(shared_ptr<Foo> sp);
};

class Foo : public enable_shared_from_this<Foo>
{
public:
    void test();
    void print();
};

void Bar::test(shared_ptr<Foo> sp)
{
    // 此时sp.use_count()为3
    sp -> print();
}

void Foo::test()
{
    // 若换成shared_ptr<Foo> sp = shared_ptr<Foo>(this);会出错
    // 这相当于使用一个原生指针分别构造两个shared_ptr,会造成重复释放的问题
    shared_ptr<Foo> sp = shared_from_this();  // 此时sp.use_count()为2
    Bar bar;
    bar.test(sp);
    // 此时sp.use_count()为2
}

void Foo::print() { cout << "in Foo::print" << endl; }

int main()
{
    shared_ptr<Foo> sp1(new Foo);
    sp1 -> test();
    // 此时sp1.use_count()为1

    // !!!错误用法,抛出bad_weak_ptr异常!!!
    // 原因(参考boost的enable_shared_from_this.hpp):enable_shared_from_this有一个weak_ptr类型的成员,
    // 它只能由shared_ptr通过enable_shared_from_this的一个成员函数赋值(故使用以下方式时,该成员为空),
    // shared_from_this()正是通过该成员“获取”到shared_ptr
    Foo *fp = new Foo;
    fp -> test();
    delete fp; 
}

  shared_ptr对象可以在共享一个指针P(owned pointer)所有权(负责其释放)的同时保存另一个指针P'(stored pointer,不负责其释放)。通常P和P'是相同的,但通过别名构造器(aliasing constructor)可使shared_ptr拥有不同的P和P':

struct C
{
    C() : data(new int(10)) {}
    ~C() {delete data; data = NULL;}
    int *data;
};

int main ()
{
    std::shared_ptr<C> obj(new C);
    // sp1和sp2的use_count均为3
    // shared_ptr的*运算符和get()函数操作的均是其保存的指针P',如*sp1==10
// 另外,注意sp1和sp2的类型是其保存的指针P'的类型! std::shared_ptr<int> sp1(obj, obj -> data); std::shared_ptr<int> sp2(sp1);
return 0; }

  关于类型转换:

template <class T, class U>
shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp) noexcept;

  static_pointer_cast即shared_ptr的static_cast:(sp非空时)返回的对象与sp共享owned pointer,且将sp的stored pointer转换为T*类型(这里需要确保static_cast<T*>(sp.get())是合法的)。如下例:

class Test {};
class Base {};
class Derived : public Base {};

int main()
{
    std::shared_ptr<Test> tsp(new Test);
    std::shared_ptr<Base> bsp0(new Base);
    std::shared_ptr<Base> bsp(tsp, bsp0.get());
    std::shared_ptr<Derived> dsp = std::static_pointer_cast<Derived>(bsp);
    // 现在tsp、bsp0、bsp和dsp的use_count分别为3、1、3、3

    return 0;
}

  关于排序:重载的<运算符考虑的是stored pointer的地址;而owner_before()考虑的则是owned pointer的地址,它的排序结果在shared_ptr被用于关联容器(如map)的key时使用,两个shared_ptr对象仅在共享同一指针或均为空时相等。

 

  2、weak_ptr:Weak shared pointer

int main()
{
    shared_ptr<int> sp1(new int(10));

    // 可以用shared_ptr/weak_ptr构造weak_ptr
    // weak_ptr拥有资源的访问权,但没有所有权(不会增加引用计数)
    weak_ptr<int> wp(sp1);  // wp和sp1的use_count()均为1

    if (!wp.expired())
    {
        // lock():从weak_ptr“获取”一个可用的shared_ptr对象
        shared_ptr<int> sp2 = wp.lock();  // wp和sp1的use_count()均为2
        *sp2 = 100;
    }

    // 此时wp和sp1的use_count()均为1

    return 0;
}

  expired():检查weak_ptr对象是否失效(为空或已经没有shared_ptr和它一起观测某个资源)。和空weak_ptr一样,失效的weak_ptr不能用于还原出shared_ptr(lock())。expired()和use_count()==0返回相同的结果,但效率更高。示例:

weak_ptr<int> wp;    // 此时wp.expired()为1

shared_ptr<int> sp1(new int);
wp = sp1;
shared_ptr<int> sp2 = wp.lock();

// 此时wp.expired()为0

sp2.reset();    // 此时wp.expired()为0
sp1.reset();    // 此时wp.expired()为1

  另外,weak_ptr没有重载*和->运算符,不能像普通指针一样使用(要借助lock(),参考第一个例子中的sp2)。

  shared_ptr还有“环状引用”的问题,也需要使用weak_ptr来规避,如下例:

class parent;
class children;
typedef shared_ptr<parent> parent_ptr;  // 改为typedef weak_ptr<parent> parent_ptr;方可避免“环状引用”的问题
typedef shared_ptr<children> children_ptr;  // 改为typedef weak_ptr<children> children_ptr;方可避免“环状引用”的问题

class parent
{
public:
    children_ptr children;
};

class children
{
public:
    parent_ptr parent;
};

int main()
{
    shared_ptr<parent> father(new parent);
    shared_ptr<children> son(new children);
    father -> children = son;
    son -> parent = father;  // father和son的use_count()均为2,程序退出时没有调用parent和children的析构函数!!!

    return 0;
}

 

  3、unique_ptr:

  1)在了解unique_ptr之前,先来回顾一下auto_ptr。

  auto_ptr:Automatic Pointer(自C++11起已弃用)。

  auto_ptr对象对它管理的指针拥有所有权,即负责其内存释放(释放时机:auto_ptr对象销毁时)。

  和shared_ptr一样,用同一个原生指针初始化多个auto_ptr对象会导致重复释放内存的错误。

class Base
{
public:
    Base() {cout << "in Base" << endl;}
    ~Base() {cout << "in ~Base" << endl;}
    void test() {cout << "in test" << endl;}
};

int main()
{
    auto_ptr<int> ap1(new int);
    // 基本操作
    *ap1 = 10;
    int *p = ap1.get();  // *p == 10
    auto_ptr<Base> ap2(new Base);
    ap2 -> test();

    auto_ptr<Base> ap3 = ap2; // 赋值或“复制”构造时会转移所有权(故不能用于STL容器中):ap2.get()变成NULL
    ap3 -> test();

    auto_ptr<Base> ap4(new Base);
    Base *bp = ap4.release();  // ap4.get()变成NULL。释放所有权,不再对之前管理的指针(的内存)负责。返回之前管理的指针
    delete bp;

    auto_ptr<Base> ap5(new Base);
    ap5.reset(); // “显式”销毁/析构其指向的对象。可设置指向新的对象

    return 0;
}

  2)unique_ptr:管理指针(负责其内存释放),提供有限的垃圾回收机制,相比于原生指针几乎没有额外开销(取决于使用的deleter)。

  (1)只支持move赋值/构造,不支持拷贝赋值/构造(因为它们被定义为deleted function):

int main ()
{
    std::unique_ptr<int> foo(new int (101));
    std::unique_ptr<int> bar;

    // bar = foo;  // 报错:use of deleted function
    bar = std::move(foo);  // 此时foo == nullptr,*bar == 101

    return 0;
}
// 拷贝构造、拷贝赋值均不被允许
unique_ptr (const unique_ptr&) = delete;
unique_ptr& operator= (const unique_ptr&) = delete;

 

 

 

  参考资料:

  http://www.cplusplus.com

 

 

不断学习中。。。

posted on 2015-12-06 01:31  han'er  阅读(400)  评论(0编辑  收藏  举报

导航