C++智能指针——share_ptr详解

前言
std::shared_ptr是在c++11中引入的一种智能指针,其特点是它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源。在c++中使用shared_ptr需要包含头文件。
共享指针是靠引用计数的方式来实现共享的功能,其中引用计数可以通过智能指针与引用计数详解文章了解。

std::shared_ptr常用函数介绍

  • std::shared_ptr的初始化有两种方法:
    一个是构造函数,一个是std::make_shared辅助函数(该函数不是成员函数)
构造函数实现:
int *aa = new int(10)
shared_ptrp1(aa);
============================
辅助函数实现:
std::shared_ptr foo = std::make_shared (10);
  • get函数,表示返回当前存储的指针(就是被shared_ptr所管理的指针)
  • use_count函数,表示当前引用计数
  • reset函数,表示重置当前存储的指针。
  • operator*,表示返回对存储指针指向的对象的引用。它相当于:* get()。
  • operator->,表示返回指向存储指针所指向的对象的指针,以便访问其中一个成员。跟get函数一样的效果。
int main(int argc, char *argv[]) {

    int *aa = new int(10)
    shared_ptrp1(aa);
	
    cout<<"p1 = "<<*p1.get()<<", p1 count = "<p2(p1);
    cout<<"p2 = "<<*p2.get()<<", p2 count = "<p3 = p2;
    cout<<"p3 = "<<*p3.get()<<", p3 count = "<p1 = 10, p1 count = 1
p2 = 10, p2 count = 2
p3 = 10, p3 count = 3
p1 = 20, p1 count = 1
p1 = 20
a = 5, b = 9

shared_ptr使用陷阱:

1、不能直接将其它指针赋值给shared_ptr。

shared_ptr p1 = new int(10);  //不能隐式转换,类型不匹配

2、切记使用独立两个独立的shared_ptr来存储同一个指针

// 由于p1和p2是两个不同对象,但是管理的是同一个指针,这样容易造成空悬指针,比如p1已经将aa delete了,这时候p2里边的aa就是空悬指针了
int *aa = new int(10)
shared_ptr p1(aa);
shared_ptr p2(aa);

3、shared_ptr 作为被保护的对象的成员时, 小心因循环引用造成无法释放资源(可以结合weak_ptr来解决);

例如testA中有一个shared_ptr保护的testB成员,testB中又有一个shared_ptr保护的A成员,当他们各自调用自己的成员函数时就造成循环引用,这样他们的析构函数就无法执行了(可以理解成两个普通对象相互持有对方引用问题)

#ifndef testA_H
#define testA_H

#include 
using namespace std;

class testB;
class testA {
    public:
    testA();
    ~testA();
    void testFunA(const shared_ptr &sptr);

    private:
    shared_ptr m_sptrB;
};
#endif

=====================================================================
#include "testA.h"
#include 
using namespace std;

testA::testA(){}

void testA::testFunA(const shared_ptr &sptr) {
    cout<<"testFunA"<

using namespace std;
class testA;
class testB {
    public:
    testB();
    ~testB();

    void testFunB(const shared_ptr &sptr);

    private:
    shared_ptrm_sptrA;
};
#endif

=====================================================================
#include "testB.h"
#include 
using namespace std;

testB::testB(){}

void testB::testFunB(const shared_ptr &sptr) {
    cout<<"testFunB"< sptra(ta);
    shared_ptr sptrb(tb);

    sptra->testFunA(sptrb);
    sptrb->testFunB(sptra);
}

输出:

testFunA
testFunB

4、多线程环境中使用共享指针的代价非常大,为保证线程安全需要加锁,要考虑因为 share_ptr 维护引用计数而造成的上下文切换开销;

5、shared_ptr有默认的删除器,但是只负责删除new出来的内容,管理的资源不是new分配的内存,则需要自己定义一个删除器,另外也不支持数组内存回收(默认删除器只是简单的 delete xx)

#ifndef TESTDELETE_H
#define TESTDELETE_H

class TestDelete {
    public:
    TestDelete(int val);
    ~TestDelete();

    private:
    int val;
};
#endif

======================================================
#include "testDelete.h"
#include 
using namespace std;

TestDelete::TestDelete(int val)
    :val(val)
{
}

TestDelete::~TestDelete() 
{
    cout<<"~TestDelete destructor"<
struct CustomDelete
{
    void operator()(T* td) {
        delete[] td;
    }
};


int main(int argc, char const *argv[])
{
    TestDelete *arrayTD = new TestDelete[3]{TestDelete(1), TestDelete(2), TestDelete(3)};
    shared_ptr ptrd(arrayTD);   //该行代码执行之后TestDelete的析构函数不会执行
    shared_ptr ptrd(arrayTD, CustomDelete()); //带有删除器会执行三次析构函数


    TestDelete td;
    TestDelete* tdd = &td;
    shared_ptr sptrtdd(tdd); //这种方式不会回收管理指针对象
}

6、不要不加思考地把指针替换为shared_ptr来防止内存泄漏,shared_ptr并不是万能的,使用它们是需要一定的开销的;

通常在项目开发过程中,如果有很多地方都要使用某个对象指针,如果用普通指针可能会出现忘记回收导致内存泄漏的情况,或者删除指针对象时机太早,导致空悬指针问题,这时候可以尝试使用智能指针来解决问题。

下一章:weak_ptr详解

参考:http://www.cplusplus.com/reference/memory/shared_ptr/

你可能感兴趣的:(C++学习)