前序内容:
为了实现智能指针在拥有多个实例的运用场景(例如多个线程)仍然可以自动管理内存块,C++11引入了shared_ptr。
#include
#include
#include
class X
{
public:
X() {
strcpy_s(buffer, 32, "123456789");
std::cout << "X
";
}
~X() { std::cout << "~X
"; }
X(const X&other) { std::cout << "enter copy
"; };
X &operator=(const X&other) { std::cout << "enter =
"; };
X &operator=(const X&&other) { std::cout << "enter move
"; return *this; };
X(X&& other) { std::cout << "enter move-con
"; };
void test()
{
std::cout << "enter test
";
}
char buffer[32];
};
void closeFile(std::FILE* fp)
{
std::cout << "Close File.
";
std::fclose(fp);
}
class MyOperator
{
public:
MyOperator() = default;
~MyOperator() = default;
void operator()(X* x) const {
std::cout << "operator delete X
";
delete x;
};
};
int main()
{
{
std::shared_ptr sPtr1(nullptr); //空指针,不触发析构
}
std::cout<<"e.g 1 finish!"< b = std::make_shared();
//采用new初始化
std::shared_ptr < X > c(new X);
}
std::cout<<"e.g 2 finish!"< a(new X[3]);
}
std::cout<<"e.g 3 finish!"< fp(std::fopen("./data.txt", "w"),
closeFile);
fprintf(fp.get(), "123
");
}
std::cout<<"e.g 4 finish!"< xm(new X, MyOperator());
auto mop = MyOperator();
std::shared_ptr xm2(new X, mop);
}
std::cout<<"e.g 5 finish!"< 以下是运行结果:
e.g 1 finish!
X
X
~X
~X
e.g 2 finish!
X
X
X
~X
~X
~X
e.g 3 finish!
Close File.
e.g 4 finish!
X
X
operator delete X
~X
operator delete X
~X
e.g 5 finish! std::shared_ptra ( new X());
std::shared_ptr b = a;
std::cout << "b: " << static_cast(&b) << "
";
std::cout << "b.get(): " << static_cast(b.get()) << "
";
std::cout << "b->buffer: " << static_cast(b->buffer) << "
";
std::cout << "a: " << static_cast(&a) << "
";
std::cout << "a.get(): " << static_cast(a.get()) << "
";
std::cout << "a->buffer: " << static_cast(a->buffer) << "
"; 运行结果:
b: 0000007B641EF2F8
b.get(): 00000242BF16D1A0
b->buffer: 00000242BF16D1A0
a: 0000007B641EF2C8
a.get(): 00000242BF16D1A0
a->buffer: 00000242BF16D1A0可以看到a和b地址不一样,是两个实例,但是管理的资源地址都是一致的。
这是网上查询到的原理性质的实现方式:
#include
#include
#include
using namespace std;
template
class Shared_Ptr{
public:
Shared_Ptr(T* ptr = nullptr)
:_pPtr(ptr)
, _pRefCount(new long(1))
, _pMutex(new mutex)
{}
~Shared_Ptr()
{
Release();
}
Shared_Ptr(const Shared_Ptr& sp)
:_pPtr(sp._pPtr)
, _pRefCount(sp._pRefCount)
, _pMutex(sp._pMutex)
{
AddRefCount();
}
Shared_Ptr& operator=(const Shared_Ptr& sp)
{
//if (this != &sp)
if (_pPtr != sp._pPtr)
{
// 释放管理的旧资源
Release();
// 共享管理新对象的资源,并增加引用计数
_pPtr = sp._pPtr;
_pRefCount = sp._pRefCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return *this;
}
T& operator*(){
return *_pPtr;
}
T* operator->(){
return _pPtr;
}
int UseCount() { return *_pRefCount; }
T* Get() { return _pPtr; }
void AddRefCount()
{
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
_pMutex->lock();
if (--(*_pRefCount) == 0)
{
delete _pRefCount;
delete _pPtr;
deleteflag = true;
}
_pMutex->unlock();
if (deleteflag == true)
delete _pMutex;
}
private:
long *_pRefCount;
T* _pPtr;
mutex* _pMutex;
}; 大致的思路:
管理2个资源,第一个是被管理的资源对应的指针,第二个是计数器指针,计数器是一个长整型变量,初始值为1,当发生复制时,计数器+1,当发生Release或者析构时,计数器-1,当计数器为0,则触发真正的资源释放函数。
查询STL标准代码,采用的是原子操作,而不是示例中的互斥操作,效率更高。详细解析推荐查看:源码分析shared_ptr实现 - 知乎
想象一下链表的基本单元节点的写法:
struct Node
{
int data;
Node *next;
};将其转换为shared_ptr:
struct Node
{
int data;
std::shared_ptr pNext;
~Node() { std::cout << "~Node" << std::endl; }
}; 如果此时使用循环链表:
auto nodeA = std::make_shared();
auto nodeB = std::make_shared();
nodeA->pNext = nodeB;
std::cout << "A use_count:" << nodeA.use_count() << std::endl;
std::cout << "B use_count:" << nodeB.use_count() << std::endl;
nodeB->pNext = nodeA;
std::cout << "##########" << std::endl;
std::cout << "A use_count:" << nodeA.use_count() << std::endl;
std::cout << "B use_count:" << nodeB.use_count() << std::endl; 则nodeA和nodeB都不会资源释放
查询shared_ptr的引用计数,发现再第6行代码后A和B都为2。
A use_count:1
B use_count:2
##########
A use_count:2
B use_count:2weak_ptr是shared_ptr的一种弱引用,实际使用时要调用lock函数来获取实际的对象。
将刚才的代码修改为weak_ptr就可以正常释放了
struct Node2
{
std::weak_ptr pNext;
~Node2() { std::cout << "~Node2" << std::endl; }
};
int main()
{
auto nodeA = std::make_shared();
auto nodeB = std::make_shared();
nodeA->pNext = nodeB;
std::cout << "A use_count:" << nodeA.use_count() << std::endl;
std::cout << "B use_count:" << nodeB.use_count() << std::endl;
nodeB->pNext = nodeA;
std::cout << "##########" << std::endl;
std::cout << "A use_count:" << nodeA.use_count() << std::endl;
std::cout << "B use_count:" << nodeB.use_count() << std::endl;
} 另外的,当weak_ptr完成资源的捕获后,还能够在原始的shared_ptr被释放后,临时的延长资源的生命周期。
auto nodeA = std::make_shared();
auto threadfun = [&nodeA]()
{
std::weak_ptr c = nodeA;
auto getA = c.lock();
std::cout << "get lock" << std::endl;
std::this_thread::sleep_for(2s);
getA->data = 1;
std::cout << "finish!" << std::endl;
};
std::thread t1(threadfun);
std::this_thread::sleep_for(100ms);
nodeA.reset();
std::cout << "get reset" << std::endl;
t1.join(); 在nodeA reset后,由于weak_ptr已经通过lock获取得到资源,所以仍然可以操作,运行结果如下:
get lock
get reset
finish!
~Node2 | 留言与评论(共有 0 条评论) “” |