C++11智能指针知识汇总与整理

一、智能指针原理

        智能指针是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保在离开指针所在作用域时,自动正确地销毁动态分配的对象,防止内存泄露。它一种通用实现技术是使用引用计数,每使用它一次,内部的引用计数加1,每析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。

        C++11提供了3种智能指针:std::shared_ptr、std::unique_ptr、std::weak_ptr,使用时加入

二、shared_ptr指针

        std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构时候,内存才会被释放。

1、初始化

1)通过构造函数初始化

std::shared_ptr p(new int(1));

std::shared_ptr p2 = p;

2)通过std::make_shared初始化

std::shared_ptr p = std::make_shared(10);

3)通过reset方法初始化

std::shared_ptr ptr;
ptr.reset(new int(1));
if(ptr)
{
    std::cout<< "ptr is not null";
}

2、获取原始指针

当需要获取原始指针时候,可以使用get方法获取原始指针

std::shared_ptr ptr(new int(1));
int *p = ptr.get();

3、指定删除器

智能指针初始化可以指定删除器,如下:

void DeleteInitPtr(int* p)
{
    delete p;
}

std::shared_ptr p(new int, DeleteInitPtr);

当p的引用计数为0时,自动调用删除器DeleteInitPtr来释放对象内存,删除器也可以写成lambda表达式,因此可以改写成:

std::shared_ptr p(new int, [](int* p){delete p;});

(1)当std::shared_ptr管理动态数组时,也需要指定删除器,因为std::shared_ptr默认不支持数组对象,
代码如下:
    std::shared_ptr p(new int[10], [](int* p){delete[] p;});

(2)通过std::default_delete作为删除器管理数组:
std::shared_ptr p(new int[10], std::default_delete);

(3)通过make_shared_array让std::shared_ptr支持数组:
template
shared_ptr make_shared_array(size_t size)
{
    return std::shared_ptr p(new T(size), std::default_delete());
}

测试代码:
std::shared_ptr p = make_shared_array(10);
std::shared_ptr p = make_shared_array(10);

4、使用shared_ptr需要注意的问题

智能指针虽然可以自动管理堆内存,但它还是有不少陷阱,在使用时要注意。

1)不要用一个原始指针初始化多个shared_ptr,例如下面这些是错误的:

int *ptr = new int;
shared_ptr p1(ptr);
shared_ptr p2(ptr); 逻辑错误

2)不要在函数实参中创建shared_ptr。如下:
function(shared_ptr(new int), g());

这是由于不同编译器不同调用约定决定,可能引发内存泄露,正确写法:
shared_ptr p(new int());
f(p, g());

3)通过shared_from_this()返回指针

不要将this作为shared_ptr返回来,因为this指针本质上是一个裸指针,因此这样可能会导致重复析构,如下:

struct A
{
    shared_ptrGetSelf()
    {
            return shared_ptr(this); //不要这样做
    }
};

int main()
{
    shared_ptr
sp1(new A);
    shared_ptr
sp2 = sp1->GetSelf();
    return 0;
}

在此例中由于用同一个指针(this)构造了两个智能指针sp1和sp2,而它们之间没有任何关系,在离开作用域之后,this将会被构造的
两个智能指针各自析构,导致重复析构的错误。
正确返回this的shared_ptr做法:让目标类通过派生std::enable_shared_from_this类,然后使用积累的成员函数shared_from_this来
返回this的shared_ptr,如下:
class A: public std::enable_shared_from_this

{
    std::shared_ptr
GetSelf()
    {
        return shared_froml_this();
    }
};

std::shared_ptr spy(new A);
std::shared_ptr
p = spy->GetSelf();

4)要避免循环引用:
struct A;
struct B;

struct A
{
    std::shared_ptr bptr;
    ~A() { cout << "A is deleted!" << endl; };
};

struct B
{
    std::shared_ptr
aptr;
    ~B() { cout << "B is deleted!" << endl; };
};

void testPtr()
{
    std::shared_ptr
ap(new A);
    std::shared_ptr bp(new B);
    ap->bptr = bp;
    bp->aptr = ap;
}

测试结果是两个指针A和B都不会被删除,存在内存泄露,循环引用导致ap和bp的引用计数为2,在离开作用域后,引用计数均减为1不会减为0,导致两个指针不会被析构,导致内存泄露。解决办法是将A和B中任一成员智能指针类型改为std::weak_ptr。

三、unique_ptr指针

        unique_ptr独占型智能指针,它不允许其他的智能指针共享其内部的指针。

特点:

1、不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。如:

unique_ptr myPtr(new T);
unique_ptr myOtherPtr = myPtr; //错误,不能复制

2、可以通过std::move转移unique_ptr所有权

unique_ptr myPtr(new T);
unique_ptr myOtherPtr = std::move(myPtr);

3、unique_ptr不可以用make_shared方法创建智能指针,C11目前没有make_unique方法

4、unique_ptr可以指向一个数组,如:

std::unique_ptr ptr(new int[10]);
ptr[9] = 9;                                    //设置最后一个元素为9

5、unique_ptr指定删除器

unique_ptr指定删除器时需要确定删除器类型,所以不能像shared_ptr那样直接指定删除器,可以这样写:
std::unique_ptr ptr(nwe int(1), [](int*p){delete p;});此写法在lambdb表达式没有捕获变量时候是正确的,如果捕获了变量则会编译报错。
如果希望unique_ptr的删除器支持lambda表达式,可以这样写:
std::unique_ptr> ptr(new int(1), [&](int*p){delete p;});
自定义std::unique_ptr删除器:
#include
#include
using namespace std;

struct MyDeleter
{
        void operator()(int*p)
        {
                cout<<"delete"<                 delete p;
        }
};

int main()
{
        std::unique_ptr p(new int(1));
        return 0;
}

四、weak_ptr弱引用指针

        弱引用指针weak_ptr是用来监视shared_ptr的,不会使引用计数加1,它不管理shared_ptr内部指针,主要是为了监视shared_ptr的生命周期,更像是shared_ptr的一个助手。weak_ptr没有重载操作符*和->,因为它不共享只恨,不能操作资源,主要是通过shared_ptr呼气的资源的监测权,它的构造与析构并不会引起引用计数的增加与减少。存粹是为了观察共享指针资源的存在性。

1、通过use_count()方法来获得当前观测资源的引用计数,如下:

shared_ptr sp(new int(10));
weak_ptr wp(sp);

cout<

2、通过expired()方法来判断所观测的资源是否已经被释放

shared_ptr sp(new int(10));
weak_ptr wp(sp);

if(wp.expired())
{
    cout<<"weak_ptr无效,所监视的资源已被释放!\n"< }
else
{
    cout<<"weak_ptr有效"< }

3、通过lock方法获取所监视的shared_ptr,如下:

std::weak_ptr gw;
void f()
{
    if(gw.expired())
    {
        std::cout<<"gw is expired\n";
    }
    else
    {
        auto spt = gw.lock();
        std::cout <<*spt << "\n";
    }
}

int main()
{
    {
        auto sp = std::make_shared_ptr(42);
        gw = sp;
        f();
    }
    f();
}
输出如下:
42
gw is expired

4、weak_ptr返回this指针

在前面提到不能直接将this指针返回为shared_ptr,需要通过std::enable_shared_from_this类,并通过其方法shared_from_this来返回智能指针,原因是std::enable_shared_from_this类中有一个weak_ptr指针,这个weak_ptr是用来观测this智能指针的,调用shared_from_this()方法时,会调用内部这个weak_ptr的lock()方法,将所观测的shared_ptr返回。

回顾前例:

class A: public std::enable_shared_from_this
{
    std::shared_ptr
GetSelf()
    {
        return shared_froml_this();
    }
};

std::shared_ptr spy(new A);
std::shared_ptr
p = spy->GetSelf();

在外面创建A对象的智能指针和通过该对象返回this的智能指针都是安全的,因为shared_from_this()是背不得weak_ptr调用lock()方法之后返回的智能指针。

5、weak_ptr解决循环引用问题

struct A;
struct B;

struct A
{
    std::shared_ptr bptr;
    ~A() { cout << "A is deleted!" << endl; };
};

struct B
{
    std::shared_ptr
aptr;
    ~B() { cout << "B is deleted!" << endl; };
};

void testPtr()
{
    std::shared_ptr
ap(new A);
    std::shared_ptr bp(new B);
    ap->bptr = bp;
    bp->aptr = ap;
}

在上例子中由于循环引用导致ap和bp引用计数都为2,离开作用域后引用计数都减为1,不会删除指针,导致内存泄露。可以通过weak_ptr解决这个问题,只需要将A或者B的任一个成员改成weak_ptr即可。

struct A;
struct B;

struct A
{
    std::shared_ptr bptr;
    ~A() { cout << "A is deleted!" << endl; };
};

struct B
{
    std::weak_ptr
aptr;
    ~B() { cout << "B is deleted!" << endl; };
};

void testPtr()
{
    std::shared_ptr
ap(new A);
    std::shared_ptr bp(new B);
    ap->bptr = bp;
    bp->aptr = ap;
}

这样在对B的成员赋值时,即执行bp->aptr=ap;时,由于aptr是weak_ptr,它并不会增加引用计数,所以ap的引用计数仍然会是1,在离开作用域之后,ap的引用计数会减为0,A指针会被析构,析构后其内部的bptr的引用计数会减为1,然后在离开作用域后bp引用计数又从1减为0,B对象也将被析构,不会发生内存泄露。

总结:智能指针是为没有垃圾回收机制的语言解决可能得内存泄露问题的利器,但是在实际使用时有一些需要注意的地方,好在这些问题都可以解决。

1)shared_ptr与unique_ptr如何选择:如果希望只有一个只能指针管理资源或者管理数组,可以用unique_ptr;如果希望多个智能指针管理同一个资源,可以用shared_ptr。

2)weak_ptr是shared_ptr的助手,只是监视shared_ptr管理的资源是否被释放,本身并不操作和管理资源。用于解决shared_ptr的循环引用问题和返回this指针的问题。

你可能感兴趣的:(C++,c++,开发语言,智能指针)