1.为什么需要只能指针?
写代码时,离不开申请空间,而申请的空间都赋予了指针,指针自己申请的空间,需要用户对其进行管理。
2.智能指针的使用和原理
我们可以利用对象的生命周期来控制程序的资源。
在对象构造时获取资源,控制这对资源的访问,在对象生命周期内始终保持有效,最后在对象析构的时候,释放资源。
所以我们可以把管理一份资源交给一个对象。两点好处:
1.不需要自己显示的释放资源;
2.这份资源在对象的生命周期内始终有效。
我们在这份原理上,再在类中重载*,->就可以让这个对象像指针一样使用。3.
3.四个智能指针
1.auto_ptr
原理:管理权转移的思想。
问题:当对象拷贝或者赋值后,前边的对象就会悬空。
所以auto_ptr的问题,非常明显,所以实际上就不能使用这个智能指针。
template <class T>
class autoptr
{
public:
autoptr(T*ptr=nullptr)
:_ptr(ptr)
{}
~autoptr()
{
if (_ptr)
delete _ptr;
}
autoptr(autoptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;//复制以后将ap中的资源转移带当前资源当中,ap为空。
}
autoptr<T>& operator=(autoptr<T>& ap)
{
if (this != ap)
{
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
T& operator*(){ return *_ptr; }
T * operator ->(){ return _ptr; }
private:
T *_ptr;
};
int main()
{
autoptr<int> ap1(new int(10));
autoptr<int> ap2 = ap1;
return 0;
}
2.unique_ptr
原理:防止拷贝或者赋值
将这个智能指针中的拷贝构造函数和赋值运算符重载函数直接私有化。
template< class T>
class uniqueptr
{
public:
uniqueptr(T*ptr = nullptr)
:_ptr(ptr)
{}
~uniqueptr()
{
if (_ptr)
delete _ptr;
}
T& operator*(){ return *_ptr; }
T * operator ->(){ return _ptr; }
private:
T *_ptr;
//uniqueptr(uniqueptr<T> const &) ;
//uniqueptr & operator=(uniqueptr<T>const &) ;
uniqueptr(uniqueptr<T> const &)=delete;
uniqueptr & operator=(uniqueptr<T>const &)=delete;
};
3.shared_ptr
原理:通过引用计数的方式来实现多个shared_ptr对象之间的共享资源。
1.shared_ptr在其内部,每一个资源都维护着一份计数,用来记录这份资源被多少个资源共享;
2.在对象销毁时,对象的引用数减一;
3.如果计数是0,说明自己是最后一个使用这个对象的,必须释放资源;
4.如果不是0,说明还有别的对象在使用,就不能释放资源。
template<class T>
class sharedptr
{
public:
sharedptr(T*ptr = nullptr)
: _ptr(ptr)
, _count(new int(1))
, _pmutex(new mutex)
{
}
~sharedptr()
{
release();
}
sharedptr(const sharedptr<T>& sp)
: _ptr(sp._ptr)
, _count(sp._count)
, _pmutex(sp._pmutex)
{
addcount();
}
sharedptr<T>&operator = (const sharedptr<T>& sp)
{
if (_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
_count = sp._count;
_pmutex = sp._pmutex;
addcount();
}
return *this;
}
T& operator*(){ return *_ptr; }
T * operator ->(){ return _ptr; }
int getcount(){ return *_count; }
T* get(){ return get; }
void addcount()
{
_pmutex->lock();
++(*_count);
_pmutex->unlock();
}
private:
void release()
{
bool deletefalg = false;
if (--(*_count) == 0)
{
delete _count;
delete _ptr;
deletefalg = true;
}
if (deletefalg == true)
delete _pmutex;
}
int *_count;
T* _ptr;
mutex *_pmutex;
};
shared_ptr智能指针中引用计数多个智能指针共享的,两个线程中智能指针的引用计数同时++或者--,这种操作不是原子的,存在线程安全的问题,所以必须加锁。加锁后的shared_ptr使用的过程中线程是安全的。
4.weak_ptr
当两个shared_ptr智能指针循环引用的时候:
看代码:
struct listnode
{
int _data;
shared_ptr<listnode> _pre;
shared_ptr<listnode> _next;
~listnode()
{
cout << "listnode" << endl;
}
};
void test()
{
shared_ptr<listnode> node1(new listnode);
shared_ptr<listnode> node2(new listnode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_pre = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
}
int main()
{
test();
return 0;
}
分析:node1和node2两个智能指针指向两个节点,引用计数为1;
node1->next指向node2,node2->pre指向node1,引用计数+1;
_next是node1的资源,node1释放,_next才能析构,pre是node2的资源,node2释放,_pre才能析构,这就是循环引用,所以谁也不释放。就会造成内存泄漏。
解决这个问题,就要用到weak_ptr
struct listnode
{
int _data;
weak_ptr<listnode> _pre;
weak_ptr<listnode> _next;
~listnode()
{
cout << "listnode" << endl;
}
};
void test()
{
shared_ptr<listnode> node1(new listnode);
shared_ptr<listnode> node2(new listnode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_pre = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
}
int main()
{
test();
return 0;
}
所以,weak_ptr只有一个作用。就是解决shared_ptr的循环引用。