【C++ Server】五、C++智能指针基础

五、C++智能指针基础
1、void func(){
    std::shared_ptr<int> a1(new int(10));
    // 不需要delete,内存会自动地释放掉.
}

2、四种智能指针,都在#include <memory>中:
auto_ptr:C++98的,不建议再使用.
shared_ptr:通过引用计数实现指针共享.
unique_ptr:一个指针只能由一个人来使用,即一份资源某一时刻只能由一个unique_ptr来管理.
weaked_ptr:与shared_ptr搭配使用.

3、shared_ptr的使用:
std::shared_ptr<MyClass> objectPtr1(new MyClass());
objectPtr1.use_count();    // 引用计数
std::shared_ptr<MyClass> objectPtr2(objectPtr1);
objectPtr2.reset();        // use_count-1
或objectPtr2 = nullptr;    // use_count-1

std::shared_ptr<MyClass> objectPtr3;
objectPtr1.swap(objectPtr3);        //
或std::swap(objectPtr1, objectPtr3);//

auto p = objectPtr1.get();        // 获得原始指针
p->func();
objectPtr1->func();
(*objectPtr1).func();    

if(objectPtr1.unique())            // 效率比下面那句高.
if(1 == objectPtr1.use_count()) // 作用同上.

objectPtr1可以作为值传递(会改变引用计数),也可以作为常引用传递(不会改变引用计数).
要注意看需求来设计函数的接口:
void func(const MyClass& m); // func(*objectPtr1);
void func(std::shared_ptr<MyClass> m); // func(objectPtr1);
void func(const std::shared_ptr<MyClass>& m); // func(objectPtr1);

可以自定义销毁资源的函数:
void myDeleter(MyClass* p){
    delete p;
}
std::shared_ptr<MyClass> objectPtr(new MyClass(), myDeleter);

4、shared_ptr通过用weaked_ptr打破循环引用。只要当一方改用weaked_ptr就能打破。

当外部有其他shared_ptr管理weaked_ptr所管理的资源的时候,weaked_ptr所指向的指针是有效的;当外部都没有shared_ptr管理weaked_ptr所管理的资源的时候,weaked_ptr所指向的指针是无效的。
std::shared_ptr<MyClass> sharePtr(new MyClass(1));
std::weaked_ptr<MyClass> weakPtr(sharePtr);    // sharePtr的引用计数不变

auto sharePtrTmp = weakPtr.lock();    // 调用lock的时刻,如果weakPtr所管理的资源(即new MyClass()),其引用计数不为0的时候,则返回一个有效的智能指针shared_ptr,此时加上sharePtrTmp这份引用计数的话,执行完这语句后引用计数应该是大于等于2。否则返回一个空指针。

weakPtr.reset();    // 不再管理之前管理的那份资源.

weakPtr.reset(new MyClass(2));    // 不再管理之前管理的那份资源,而开始管理另一份资源.
weakPtr.lock();    // 返回空指针.

if(weakPtr.expired()){}    // weaked_ptr所管理的资源是否有效.

5、this转换为智能指针:

void MyClass::func(){
    std::shared_ptr<MyClass> sharePtr(this); // 错误做法,会造成一个资源有两个智能指针在控制,从而导致析构函数被调用了两次.
    auto sharePtr = enable_shared_from_this(); // 正确写法.
}
std::shared_ptr<MyClass> sharePtr(new MyClass());
sharePtr.func();

正确做法是继承模板类enable_shared_from_this:
class MyClass : public std::enable_shared_from_this<MyClass>{
    ...
}
但是如果继承了enable_shared_from_this,下面这句话就不推荐了,因为继承了enable_shared_from_this就意味着我们希望MyClass是一种以智能指针的形式来管理的类型:
MyClass m1;

6、使用智能指针的默认选择是std::unique_ptr,除非要共享。

std::unique_ptr<MyClass> uniPtr(new MyClass(1));
if(uniPtr){
    uniPtr->func();
    (*uniPtr).func();
}

auto p = uniPtr.get();    // 获得原始指针

uniPtr.release();    // 不再管理所管理的资源.
delete p;

uniPtr.reset();
uniPtr.reset(new MyClass(2)); // 先放原有的资源释放,再重新去管理另外一份资源.

std::unique_ptr没有拷贝构造函数,但是有移动构造函数。
void func(std::unique_ptr<MyClass> uniPtrTmp){
    ...
}
func(std::move(uniPtr));    // 移交资源给uniPtrTmp.后面不能再使用uniPtr了.

std::unique_ptr转换为std::shared_ptr:
std::shared_ptr<MyClass> sharePtr(std::move(uniPtr));

7、使用智能指针的注意事项:
①绝对不要自己手动地管理资源,也即不要自己new和delete;
②一个裸的指针不要用两个shared_ptr管理;unique_ptr也一样。
auto pMyClass = new MyClass();
std::shared_ptr<MyClass> sharePtr1(pMyClass);
std::shared_ptr<MyClass> sharePtr2(pMyClass);    // 错误写法.
因为sharePtr1和sharePtr2的引用计数是相互独立的。
③用weaked_ptr打破循环引用,用裸指针也可以;

④当需要在类的内部接口中,如果需要将this作为智能指针来使用的话,需要用该类派生自enable_shared_from_this。

⑤shared_ptr、weaked_ptr和裸指针相比,在内存占用上会大很多,并且效率上也会有影响,尤其是在多线程模式下。
std::shared_ptr<MyClass> sharePtr1(new MyClass()); // 会new两次
std::shared_ptr<MyClass> sharePtr2 = std::make_shared<MyClass>(); // 只会new一次。建议使用这个。

⑥enable_shared_from_this的shared_from_this()在构造析构函数的时候是不能使用的。

⑦某些情况下会出现内存不会降的问题,尤其是我们使用weaked_ptr来处理循环引用的时候。

⑧如果有可能,优先使用类的实例,其次再考虑使用std::unique_ptr,最后才考虑用std::shared_ptr。
MyClass m1; // 使用类的实例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值