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_ptr<int>p1(aa);
============================
辅助函数实现:
std::shared_ptr<int> foo = std::make_shared<int> (10);
  • get函数,表示返回当前存储的指针(就是被shared_ptr所管理的指针)
  • use_count函数,表示当前引用计数
  • reset函数,表示重置当前存储的指针。
  • operator*,表示返回对存储指针指向的对象的引用。它相当于:* get()。
  • operator->,表示返回指向存储指针所指向的对象的指针,以便访问其中一个成员。跟get函数一样的效果。
int main(int argc, char *argv[]) {

    int *aa = new int(10)
    shared_ptr<int>p1(aa);
	
    cout<<"p1 = "<<*p1.get()<<", p1 count = "<<p1.use_count()<<endl; //输出结果 10,1
	
    shared_ptr<int>p2(p1);
    cout<<"p2 = "<<*p2.get()<<", p2 count = "<<p2.use_count()<<endl; //输出结果 10,2 p2是调用复制构造函数,引用计数加1
	
    shared_ptr<int>p3 = p2;
    cout<<"p3 = "<<*p3.get()<<", p3 count = "<<p3.use_count()<<endl; //输出结果 10,3 p3是调用赋值函数,引用计数加1

    int *val = new int(20);
    p1.reset(val); // p1之前存储的是aa指针,调用reset之后会先回收aa指针,然后再存储val指针

    cout<<"p1 = "<<*p1.get()<<", p1 count = "<<p1.use_count()<<endl;

    cout<<"p1 = "<<*p1<<endl;

    shared_ptr<POINTTEST>p(new POINTTEST); // operator->函数是让shared_ptr使用起来跟向存储指针一样,效果相当于存储指针,因为operator->返回的就是存储指针
    p->a = 5;
    p->b = 9;
    cout<<"a = "<<p->a<<", b = "<<p->b<<endl;
    return 0;
}

输出结果:

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<int> p1 = new int(10);  //不能隐式转换,类型不匹配

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

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

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

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

#ifndef testA_H
#define testA_H

#include <memory>
using namespace std;

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

    private:
    shared_ptr<testB> m_sptrB;
};
#endif

=====================================================================
#include "testA.h"
#include <iostream>
using namespace std;

testA::testA(){}

void testA::testFunA(const shared_ptr<testB> &sptr) {
    cout<<"testFunA"<<endl;
    m_sptrB = sptr;
}
testA::~testA(){
    cout<<"~testA destructor"<<endl;
}

=====================================================================
#ifndef testB_H
#define testB_H

#include <memory>

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

    void testFunB(const shared_ptr<testA> &sptr);

    private:
    shared_ptr<testA>m_sptrA;
};
#endif

=====================================================================
#include "testB.h"
#include <iostream>
using namespace std;

testB::testB(){}

void testB::testFunB(const shared_ptr<testA> &sptr) {
    cout<<"testFunB"<<endl;
    m_sptrA = sptr;
}

testB::~testB(){
    cout<<"~testB destructor"<<endl;
}

=====================================================================

int main(int argc, char const *argv[])
{
    testA *ta = new testA();
    testB *tb = new testB();
    shared_ptr<testA> sptra(ta);
    shared_ptr<testB> 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 <iostream>
using namespace std;

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

TestDelete::~TestDelete() 
{
    cout<<"~TestDelete destructor"<<endl;
}


==========================main.cpp============================
// 定义一个简单数组删除器模板
template<class T>
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<TestDelete> ptrd(arrayTD);   //该行代码执行之后TestDelete的析构函数不会执行
    shared_ptr<TestDelete> ptrd(arrayTD, CustomDelete<TestDelete>()); //带有删除器会执行三次析构函数


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

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

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

下一章:weak_ptr详解

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

发布了88 篇原创文章 · 获赞 17 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/yj_android_develop/article/details/88895449