shared_ptr是一个引用计数智能指针,用于共享对象的所有权,也就是说它允许多个指针指向同一个对象。这一点与原始指针一致。如下:
#include<iostream>
using namespace std;
class Object
{
private:
int value;
Object(const Object&) = delete;
Object& operator=(const Object&) = delete;
public:
Object(int x = 0) :value(x) { cout << "creat Object : " << this << endl; };
~Object() { cout << "destory Object : " << this << endl; };
int& Value() { return value; }
const int& Value()const { return value; }
void Print() const { cout << value << endl; }
};
int main()
{
shared_ptr<Object> pObj(new Object(100));
cout << (*pObj).Value() << endl;
cout << "pObj 引用计数:" << pObj.use_count() << endl;
shared_ptr<Object> pObj2 = pObj;
cout << "pObj 引用计数:" << pObj.use_count() << endl;
cout << "pObj2 引用计数:" << pObj2.use_count() << endl;
return 0;
}
如上示例中可以看出:
- 跟STL里面的容器类型一样,shared_ptr也是模板类,因此在创建share_ptr时需要指定其指向的类型。
- shared_ptr指针允许多个该类型的指针共享同一个对象。share_ptr使用经典的引用计数的方法来管理对象资源,每个shared_ptr对象关联一个共享的引用计数。
(当用一个share_ptr初始化另一个share_ptr,或者 将它当作函数传递给一个函数以及作为函数的返回值时,它所关联的引用计数就会递增;当给share_ptr赋予新值或者 销毁一个share_ptr时,计数器就会递减。)
share_ptr对象的计数器编程0时,它就会自动释放自己所管理的对象。
1、创建share_ptr实例:
最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化,最后返回指向此对象的shared_ptr实例。或者不使用make_shared,可以先明确new出一个对象,然后把其原始指针传递给shared_ptr的构造函数。
如下 :
int main()
{
shared_ptr<string> pStr = make_shared<string>(10, 'a');
cout << *pStr << endl;
int* p = new int(10);
shared_ptr<int> pInt(p);
cout << *pInt << endl;
return 0;
}
2、访问所指对象
shared_ptr的使用方法与普通函数指针的使用方法类似,既可以使用解引用操作符*获得原始对象进而访问其各个成员,也可以使用指针访问符->来访问原始对象的各个成员。
3、拷贝与赋值操作
我们可以用一个shared_ptr对象来初始化另一个shared_ptr实例。该操作都会增加其引用计数。
示例:
int main()
{
shared_ptr<Object> pObj(new Object(100));
cout << (*pObj).Value() << endl;
cout << "pObj 引用计数:" << pObj.use_count() << endl;//1
shared_ptr<Object> pObj2(pObj);
cout << "pObj 引用计数:" << pObj.use_count() << endl;//2
cout << "pObj2 引用计数:" << pObj2.use_count() << endl;//2
如果shared_ptr实例p和另一个shared_ptr实例q所指向的类型相同或者可以相互转换,我们还可以进行诸如p=q这样的赋值操作。
该操作会递减p的引用计数,递增q的引用计数值。
示例:
int main()
{
shared_ptr<Object> pObj1 = make_shared<Object>("a object");
shared_ptr<Object> pObj2 = make_shared<Object>("b object");
cout << pObj1.use_count() << endl;
cout << pObj2.use_count() << endl;
pObj1 = pObj2;
cout << pObj1.use_count() << endl;
cout << pObj2.use_count() << endl;
cout << pObj1->Value() << endl;
cout << pObj2->Value() << endl;
}
结果:
4、检查引用计数
shared_ptr提供了两个函数来检查其共享的引用计数,分别是unique()和use_count().
use_count()函数用来测试当前指针的引用计数值,但是use_count()可能效率旱地,应该只把它用于调试或测试。
unique()函数用来测试该shared_ptr是否是原始指针的唯一拥有者。
5、shared_ptr仿写
#include<iostream>
#include<memory>
using namespace std;
namespace wwn
{
template<typename T>
class RefCnt//引用计数
{
private:
T* mPtr;
std::atomic<int> mCnt;
public:
RefCnt(T* ptr = nullptr) :mPtr(ptr),mCnt(1){}
void addRef() { ++mCnt; }
int delRef() { return --mCnt;}
int load() { return mCnt.load(); }
~RefCnt() {}
};
template<typename T>
struct MyDeletor//删除器
{
void operator()(T* ptr)const
{
if (ptr == nullptr) return;
delete ptr;
}
};
template<class T,typename Deletor=MyDeletor<T>>
class shared_ptr
{
public:
typedef T* pointer;
typedef T element_type;
typedef Deletor delete_type;
delete_type get_deleter()
{
return delete_type();
}
public:
shared_ptr(T* ptr = nullptr) : mPtr(ptr)
{
mpRefCnt = new RefCnt<T>(mPtr);
}
shared_ptr(const shared_ptr<T>& src) :mPtr(src.mPtr), mpRefCnt(src.mpRefCnt)
{
mpRefCnt->addRef();
}
shared_ptr& operator=(const shared_ptr<T>& src)
{
if (src == this)
{
return *this;
}
if (0==mpRefCnt->delRef())
{
get_deleter()(mPtr);
delete mpRefCnt;
mPtr = nullptr;
mpRefCnt = nullptr;
}
mPtr = src.mPtr;
mpRefCnt = src.mpRefCnt;
mpRefCnt->addRef();
return *this;
}
~shared_ptr()
{
if (0 == mpRefCnt->delRef())
{
get_deleter()(mPtr);
delete mpRefCnt;
}
mPtr = nullptr;
mpRefCnt = nullptr;
}
T* get()const { return mPtr; }
T& operator*()const { return *get(); }
T* operator->()const { return get(); }
long use_count() const { return mpRefCnt->load();}
void reset(T* p = nullptr)
{
if (0==mpRefCnt->delRef())
{
get_deleter()(mPtr);
delete mpRefCnt;
}
mPtr = p;
mpRefCnt = new RefCnt<T>(mPtr);
}
operator bool()const
{
return get() != nullptr;
}
bool unique()const
{
if (mPtr != nullptr && use_count() == 1)
{
return true;
}
return false;
}
private:
T* mPtr;
RefCnt<T>* mpRefCnt;
};
}
class Object
{
private:
int value;
Object(const Object&) = delete;
Object& operator=(const Object&) = delete;
public:
Object(int x = 0) :value(x) { cout << "creat Object : " << this << endl; };
~Object() { cout << "destory Object : " << this << endl; };
int& Value() { return value; }
const int& Value()const { return value; }
void Print() const { cout << value << endl; }
};
int main()
{
wwn::shared_ptr<Object> pa(new Object(10));
wwn::shared_ptr<Object> pb(pa);
cout << pa.use_count() << endl;
(*pa).Print();
pa->Print();
pa->Value() = 100;
pa->Print();
pb->Print();
return 0;
}
6、shared_ptr 的线程安全
- shared_ptr的引用计数本身是线程安全(引用计数是原子操作)。
- 多个线程同时读同一个shared_ptr对象是线程安全的。
- 如果是多个线程对同一个shared_ptr进行读和写,则需要加锁。
- 多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的,也需要加锁保护。(因为shared_ptr有两个数据成员,读写操作不能原子化,使得多线程读写同一个shared_ptr对象需要加锁。)
7、为什么要尽量使用make_shared()来申请管理对象以及引用计数的内存;调用适当的构造函数初始化对象;返回一个shared_ptr。
为了节省一次内存分配。
shared_ptr obj(new Object(10)):需要为Object和RefCnt各分配一次内存,现在用make_shared()的话,可以一次分配一块足够大的内存,供Object和RefCnt对象容身。不过Object的构造函数所需的参数要传给make_shared(),后者再传给Object::Object(),这只有在C++11里通过perfect forwarding才能完美解决。。