一、shared_ptr使用方法
使用该智能指针(或者其他两种)需要导入头文件#include <memory>
- 创建空指针
std::shared_ptr<int> p1; //不传入任何实参
std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr
- 明确指向
std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p4 = make_shared<int>(10); //推荐使用make_shared<T>构造
- 拷贝构造
std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p5(p3);
除此之外还可以对shared_ptr赋值,通过重写operator=实现。需要注意,对于p1=p2(均为智能指针)这种,p2所指对象由于被p1指向,所以该引用计数会加一,p1原本指向的资源的引用计数会减一。这也会引出下面关于shared_ptr指针的线程安全问题。
二、实现
//存在问题:引用计数不增加
//已解决:重载()函数换成拷贝构造函数
#include <bits/stdc++.h>
using namespace std;
template <class T>
class mShared_ptr {
private:
T* _ptr;
int* _refcount;
public:
mShared_ptr() : _ptr(new T()), _refcount(new int(0)) {}
mShared_ptr(T* obj) : _ptr(obj), _refcount(new int(1)) {}
~mShared_ptr() {
--(*_refcount);
if(_ptr && (*_refcount) == 0) {
delete _ptr;
delete _refcount;
}
}
mShared_ptr (const mShared_ptr& _other) {
++*_other._refcount;
cout << "_ref : " << *_other._refcount << endl;
this->_ptr = _other._ptr;
this->_refcount = _other._refcount;
}
mShared_ptr& operator= (const mShared_ptr& _other) {
if(_other == *this)
return *this;
--*(this->_refcount);
if(this->_ptr && *(this->_refcount) == 0) {
delete this->_ptr;
delete this->_refcount;
}
++*_other._refcount;
cout << "_ref : " << *_other._refcount << endl;
this->_ptr = _other._ptr;
this->_refcount = _other._refcount;
return *this;
}
T& operator* () {
return *_ptr;
}
T* operator-> () {
return _ptr;
}
int get_refcount() {
return *(this->_refcount);
}
};
int main(int argc, char const *argv[])
{
mShared_ptr<int> obj(new int(10));
mShared_ptr<int> obj2 = obj;
mShared_ptr<int> obj3(obj);
cout << *obj << endl;
cout << *obj2 << endl;
cout << *obj3 << endl;
cout << obj.get_refcount() << endl;
cout << obj2.get_refcount() << endl;
cout << obj3.get_refcount() << endl;
return 0;
}
运行结果如下:
root@iZuf6ccxsb4mprtz1c1mx2Z:~/Linux/Test/DataStructure# ./mShared_ptr
_ref : 2
_ref : 3
10
10
10
3
3
3
这里实现的还有一些问题,因为shared_ptr源码中关于引用计数是原子操作,不需要考虑资源使用冲突的问题,可以在自己实现的时候加锁。
三、关于shared_ptr的线程安全问题
首先什么是线程安全?
简单来说就是多个线程操作一个共享数据,都能按照预期的行为进行,无论多个线程的运行次序如何交织。
对于shared_ptr,其内部有两个变量,引用计数和真正的对象类型指针。其中引用计数是原子操作,所以对于shared_ptr的读操作是线程安全的。
但是对于shared_ptr中赋值如ptr1 = ptr2,需要两个步骤,1、ptr1的内部对象指针Obj1替换成ptr2内部对象Obj2指针;2、ptr1的对于Obj1的引用计数缓存Obj2的引用计数。
这两步并不是原子的,如果一个线程需要对shared_ptr进行赋值操作ptr1 = ptr2,刚完成第一步,就切换到其他线程又对ptr2进行操作,如ptr2 = ptr3,就有可能造成析构了引用计数。而继续之前线程的第二步,就会出错。
总之:对于shared_ptr的读操作是线程安全的。
对于shared_ptr读写操作不是线程安全的,需要加锁。
tips:为什么shared_ptr的引用计数能够同步到不同的指针中?
有人回答可能使用的是static变量,这是不可能的,因为一个类中只有一个静态变量,只能记录对于一个对象的引用次数,这在包含两个shared_ptr以上的程序中是不可行的。
个人认为是引用计数是用指针实现的,指向一个记录引用次数的对象。