C++日记——Day32:shared_ptr使用场景、陷阱、性能分析、使用建议

shared_ptr使用场景

shared_ptr<int> create0(int value)
{
    return make_shared<int>(value);
}

shared_ptr<int> myfunc(int value)
{
    shared_ptr<int> tmp = create0(value);
    return tmp;
}

int main()
{
    myfunc(11); //如果这块我们不用share_ptr变量来接受myfunc返回的结果,那么从myfunc 
                //返回的shared_ptr就会被销毁,所指向的对象也会被销毁。
    auto p11 = myfunc(12); //我们用了一个变量来接受myfunc返回值,那么myfunc返回的 
                           //sahred_ptr就不会被销毁,所指向的对象也不会被销毁。
}

 

shared_ptr使用陷阱分析:一旦用错也是致命的

1、慎用裸指针

void proc(shared_ptr<int> ptr)
{
    return;
}

int main()
{
    int *p = new int(100);
    proc(p); //语法错误,int *p不能转换成shared_ptr<int>(不支持隐式转换)
    proc(shared_ptr<int>(p)); //参数是一个临时的shared_ptr,用一个裸指针显示构造
    //此时从proc内部出来后,shared_ptr强引用计数变为0,所指向的内存被释放,因为p和 
    //shared_ptr所指向的内存是同一块内存所以shared_ptr释放后,p也不能再使用。

    *p = 45; //潜在的不可预料的问题,因为p已经被释放了
}


int main()
{
    int *p = new int(100);
    
    shared_ptr<int> p2(p);
    proc(p2); //这里proc将创建一个p2的临时对象副本去执行函数

    *p = 45; //此时可以
}

把一个普通裸指针绑定到shared_ptr上之后,内存管理的责任就交给了,shared_ptr了,这个时候你就不应该在用裸指针来访问shared_ptr所指向的内存了。推荐下面的写法:

shared_ptr<int> myp(new int(100));
proc(myp);
*myp = 34;

一定不要用裸指针初始化多个shared_ptr

int *p = new int(12);
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);   //不可以!!!!这种写法p1和p2无关联,会导致p1和p2所指向的 
                         //内存被释放两次,产生异常
shared_ptr<int> p1(new int());
shared_ptr<int> p2(p1);   //这种写法可以,p1与p2用的是同一个控制块,两者互通

 

2、慎用get()返回的指针:返回智能指针对应的裸指针

有些函数接口可能只能使用裸指针。

get返回的指针不能delete,否则会异常

shared_ptr<int> myp(new int(122));
int *p = myp.get();
delete p;   //不可以释放!!!!!!!!!!!!!!!

shared_ptr<int> myp2(p); //不可以!!!myp和myp2的引用计数都为1

不能将其他智能指针绑定到get返回的裸指针上

永远不要用get得到的指针来初始化,或者给另外一个智能指针赋值。

class CT
{
public:
    shared_ptr<CT> getself()
    {
        return shared_ptr<CT>(this); //不要用裸指针生成智能指针
    }
};

shared_ptr<CT> pct1(new CT());
shared_ptr<CT> pct2 = pct1->getself(); //不可以pct1与pct2未指向同一块内存

enable_shared_from_this<>

工作原理:enable_shared_from_this有一个弱指针,这个弱指针能够监视this在我们调用shared_from_this()这个方法时,这个方法内部实际上是调用了这个weak_ptr的lock()方法。lock()方法会让share_ptr引用计数加1,同时返回这个shared_ptr

class CT: enable_shared_from_this<CT>
{
public:
    shared_ptr<CT> getself()
    {
        return shared_from_this(); //这个就是enable_shared_from_this类中的方法,要 
                                   //通过此方法返回智能指针
   }
};

shared_ptr<CT> pct1(new CT());
shared_ptr<CT> pct2 = pct1->getself(); 

现在,在外面创建的CT对象的智能指针以及通过CT对象返回的this智能指针都是安全的

 

3、避免循环引用:

class CB;
class CA
{
public:
    shared_ptr<CB> m_pbs;
    ~CA()
    {

    }

};

class CB
{
public:
    shared_ptr<CA> m_pas;
    //weak_ptr<CA> m_pas; 变成弱引用即可
  
    ~CB()
    {

    }

};

int main()
{
    shared_ptr<CA> pca(new CA);
    shared_ptr<CB> pcb(new CB);

    pca->m_pbs = pcb;  //等价于指向CB对象的有两个强引用
    pcb->m_pas = pca;  //等价于指向CA对象的有两个强引用
    //结束后会导致无法释放,除非把其中一个的智能指针变成弱引用

}

 

性能说明

尺寸问题:

sahred_ptr的尺寸是裸指针的2倍。两个裸指针

weak_ptr的尺寸也是裸指针的2倍。两个裸指针

第一个裸指针指向的是这个智能指针所指向的对象

第二个裸指针 指向一个很大的数据结构(控制块),这个控制块里边有:所指对象的强引用计数;所指对象的弱引用计数;其他数据,比如删除器,内存分配器;

控制块是由第一个指向该对象的shared_ptr 来创建;

 

控制块创建时机:

1、make_shared:分配并初始化一个对象,返回指向此对象的shared_ptr,所以,这个make_shared他总是能够创建控制块。

2、用裸指针来创建一个shared_ptr对象时。

 

移动语义:

shared_ptr<int> p1(new int(100));
shared_ptr<int> p2(std::move(p1)); //移动语义,移动构造一个新的智能指针引用对象p2,
                                   //移动后p1就不在指向该对象(变成空),引用计数依 
                                   //旧是1

shared_ptr<int> p3;
p3 = std::move(p2); //移动赋值,p2指向空,p3指向该对象,整个对象的引用计数仍然为1

移动肯定比复制快,复制要增加引用计数,移动不需要;

移动构造函数快过复制构造函数,移动赋值运算符快过拷贝赋值运算符

 

补充说明和使用建议:

shared_ptr:

1、分配器,解决内存分配问题  shared_ptr<int> p((new int), mydelete(), mymallocator<int>())

2、谨慎使用,奇怪用法不要轻易尝试。

3、优先使用make_shared()

shared_ptr<string> ps1(new string("good luck"));  //按资料来说需要分配两次内存
auto ps2 = make_shared<string>("good luck");  //只分配一次

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值