前言
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详解