您的位置:首页 > 编程语言 > C语言/C++

C++11 shared_ptr共享智能指针

2017-07-06 15:30 459 查看

前言

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

1. shared_ptr基本使用方法

1.1 初始化

#include <memory>

//智能指针初始化
std::shared_ptr<int> p(new int(20));
cout << *p << endl;

std::shared_ptr<int> p2 = p;
cout << *p2 << endl;

std::shared_ptr<int> p3 = std::make_shared<int>();
*p3 = 1024;
cout << *p3 << endl;

p3.reset(new int(-1));
cout << *p3 << endl;

std::shared_ptr<int> p4;
if(p4)
{
cout << "ptr is null" << endl;
}
应当优先使用make_shared来构造只能指针,因为它更加高效。但是不能讲一个原始指针直接赋值给一个智能指针,例如,下面的这种情况就是错误的

std::shared_ptr<int> p = new int(20);
上面的代码会在编译的时候报错,不允许直接赋值。从上面的例子中可以看到智能指针的用法和普通指针的用法类似,只不过不需要自己管理分配的内存。shared_ptr不能通过直接将原始指针赋值来初始化,需要通过构造函数或是辅助方法来初始化。对于一个未初始化的智能指针,可以通过reset方法来初始化,当智能指针中有值的时候,调用reset会使引用计数减1。另外之智能指针可以通过重载bool类型操作符来判断智能指针是否为空(未被初始化)。

1.2 获取原始指针

使用get方法来返回原始指针

std::shared_ptr<int> p(new int(20));
int *pp = p.get();


1.3 指定删除器

智能指针的删除支持自定义删除函数,用法如下:

//自定义的删除智能指针函数
void MyDeleteIntPtr(int *p)
{
cout << __FUNCTION__ << "delte ptr" << endl;
delete p;
p = nullptr;
}

//创建智能指针
void MyMakeIntPtr()
{
cout << __FUNCTION__ << "make ptr" << endl;
std::shared_ptr<int> P(new int(1034), MyDeleteIntPtr);  //指定删除的函数
cout << "value:" << *P << endl;
}

MyMakeIntPtr();	//调用
但是这样的方式未免太过于麻烦了,那么可以写成这样的(使用lambda表达式)

void MyMakeIntPtr()
{
cout << __FUNCTION__ << "make ptr" << endl;
std::shared_ptr<int> P(new int(1034), [](int *p){
cout << __FUNCTION__ << "delte ptr" << endl;
delete p;
p = nullptr;});  //指定删除的lambda表达式
cout << "value:" << *P << endl;
}
这里调用的函数也就是仿函数了。当使用shared_ptr管理动态数组时,需要制定删除器,因为std::shared_ptr的默认删除器不支持数组对象。使用的代码如下:

std::shared_ptr<int> P(new int[1034], [](int *p){
cout << __FUNCTION__ << "delte ptr" << endl;
delete[] p;
p = nullptr;});  //指定删除的lambda表达式


也可以使用自带的std::default_delete作为删除器。default-delete的内部是通过调用delete来实现功能的,代码如下:

std::shared_ptr<int> P(new int[1034], std::defualt_delete<int[]>);


2. shared_ptr注意事项

只能只恨虽然能够自动管理内存,但它还是有不少缺陷,在使用时需要注意一下几点

2.1 不要使用一个原始指针去初始化多个shared_ptr

int * ptr = new int(1024);
std::shared_ptr<int> P(ptr);
std::shared_ptr<int> P1(ptr);	//错误


2.2 不要再函数参数实参中创建shared_ptr

function(shared_ptr<int>(new int), g());
上面的函数中写法是有缺陷的。这是因为C++的参数计算顺序在不同的编译器不同的调用约定下可能是不一样的,一般是从右到左,但是也有可能是从左到右,所以,可能的过程是先new int,然后调用g()。但是如果恰好g()发生了异常,而shared_ptr<int>还没有创建,则int内存就泄露了,正确的写法应该是这样的

shared_ptr<int> p(new int);
function(p, g());


2.3 通过shared_ptr返回this指针

struct A
{
std::shared_ptr<A> GetSelf()
{
return std::shared_ptr<A>(this);    //错误
}
};

shared_ptr<A> p1(new A());
shared_ptr<A> p2 = p1->GetSelf();
不要讲this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构。在上面的代码中构造了两个智能指针,当离开作用域之后this指针就会被两个智能指针各自析构,导致重复析构的问题。正确的使用方式是

struct A: public std::enable_shared_from_this<A>
{
std::shared_ptr<A> GetSelf()
{
return shared_from_this();
}
};

shared_ptr<A> p1(new A());
shared_ptr<A> p2 = p1->GetSelf();       //正确


4. 避免循环引用

struct A
{
std::shared_ptr<B> aptr;
~A()
{}
};

struct B
{
std::shared_ptr<A> aptr;
~B()
{}
};

shared_ptr<A> p1(new A());
shared_ptr<B> p2(new B());
p1->bptr = p2;
p2->aptr = p1;
上面的两个智能指针在构造的时候引用计数为1,但是在循环引用之后就变为了2 。在离开作用域之后两个只能指针减1,但是并不会变为0,导致两个只能指针不会被析构,产生内存泄露。解决办法是把A和B任何一个成员变量改为weak_ptr。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: