题目描述
网易一面遇到过这么一个题目,面试官要求自己实现一个线程安全的shared_ptr智能指针。
题目分析
可能很多人只知道shared_ptr是C++11模块库的头文件定义的一个智能指针,即shared_ptr模版。只要将new运算符返回的指针交给shared_ptr这个“管家”,就不必担心在哪里没有delete p了。shared_ptr在被删除的时候会自动执行delete p。通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:
shared_ptr<T> ptr(new T)
此后ptr就像T指针那样用,即ptr就是new动态分配的对象。多个shared_ptr可以共同托管一个指针p,当所有曾经托管p的shared_ptr都解除了对p的托管就会执行delete p。
这个题目拿到手后得搞清楚shared_ptr的原理
1.在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
2.在对象被销毁时,就说明自己不使用该资源了,对象的引用计数减一。
3.如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
4.如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
而且题目既然要求线程安全,就得考虑下面两个问题:
1.智能指针对象中的引用技术是多个对象所共享的,两个线程同时对智能指针进行++或–运算,这个计数原来是1,++了两次可能还是2,这样引用技术就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。
2.智能指针的对象存放在堆上,可能会导致线程安全问题。
线程安全的智能指针代码如下:
#include <utility>
#include <string>
#include <mutex>
using namespace std;
class Counter
{
public:
Counter() :
m_Counter(0)
{}
Counter(const Counter&) = delete;
Counter& operator=(const Counter&) = delete;
~Counter() {}
void reset() {
m_Counter = 0;
}
unsigned int get() const
{
return m_Counter;
}
void operator++()
{
m_Counter++;
}
void operator++(int)
{
m_Counter++;
}
void operator--()
{
m_Counter--;
}
void operator--(int)
{
m_Counter--;
}
friend ostream& operator<<(ostream& os, const Counter& c)
{
os << "Counter value: " << c.m_Counter << endl;
return os;
}
private:
unsigned int m_Counter{};
};
template<typename T>
class Shared_ptr
{
public:
explicit Shared_ptr(T *ptr=nullptr):
m_ptr(ptr),
m_pCounter(new Counter()),
m_pMutex(new std::mutex)
{
if(ptr)
{
AddRefCount();
}
}
Shared_ptr(Shared_ptr<T>& sp)
{
m_ptr = sp.m_ptr;
m_pCounter = sp.m_pCounter;
AddRefCount();
}
Shared_ptr<T>& operator=(const Shared_ptr<T>& sp)
{
if(m_ptr != sp.m_ptr)
{
Realse();
m_ptr = sp.m_ptr;
m_pCounter = sp.m_pCounter;
m_pMutex = sp.m_pMutex;
AddRefCount();
}
}
T *operator->()
{
return m_ptr;
}
T& operator*()
{
return *m_ptr;
}
T *get()
{
return m_ptr;
}
unsigned int UseCount()
{
return m_pCounter->get();
}
~Shared_ptr()
{
Realse();
}
private:
void AddRefCount()
{
m_pMutex->lock();
++(*m_pCounter);
m_pMutex->unlock();
}
void Realse()
{
bool deleteflag = false;
m_pMutex->lock();
--(*m_pCounter);
if(m_pCounter->get() == 0)
{
delete m_pCounter;
delete m_ptr;
deleteflag = true;
}
m_pMutex->unlock();
if(deleteflag == true)
delete m_pMutex;
}
private:
T *m_ptr;
std::mutex *m_pMutex;
Counter *m_pCounter;
};