C++智能指针
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C++智能指针,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5792字,纯文字阅读大概需要9分钟。
内容图文
![C++智能指针](/upload/InfoBanner/zyjiaocheng/737/3442cf5c0179404ca2715a707146f56a.jpg)
智能指针的使用和原理
RAII
RAII(resource acquisition is initaliation) 是一种利用对象生命周期来控制程序资源( 内存,文件句柄,网络连接, 互斥量等) 的技术
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源.实际上是把管理资源的任务给给一个对象,此时就可以:
- 不需要显式的释放资源
- 采取这种方式,对象所需的资源在其声明周期内始终保持有效
智能指针的原理
//在模板类中重载*,->.使smartptr可以实现指针一样的使用
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if(_ptr)
delete _ptr;
}
T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}
private:
T* _ptr;
};
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10
cout<<*sp1<<endl;
SmartPtr<int> sparray(new Date);
// 需要注意的是这里应该sparray.operator->()->_year = 2020;
// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
sparray->_year = 2020;
sparray->_month = 1;
sparray->_day = 1;
}
//C++库中智能指针都定义在memory中
#include <memory>
class Date
{
public:
Date() { cout<< "Date()" << endl;}
~Date(){ cout<< "~Date()" << endl;}
int _year;
int _month;
int _day;
}
int main ()
{
auto_ptr<Date> ap(new Date);
auto_ptr<Date> copy(ap);
//注意: auto_ptr此处存在问题,当对象拷贝或者赋值之后,前面对象悬空,程序运行出错
ap->_year = 2020;
return 0;
}
//因为上述问题,一般情况下不使用auto_ptr
int main ()
{
unique_ptr<Date> up(new Date);
//unique_ptr的设计思路--防拷贝,即不允许拷贝和赋值
}
简化模拟实现unique_ptr
tmplete <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:
//C++11防拷贝: delete
Uniqueptr(Uniqueptr<T> const&) = delte;
Uniqueptr &operator =(Uniqueptr<T> const&) = delete
//c++98防拷贝方式:只声明不实现 + 声明成私有
Uniqueptr(Uniqueptr<T> const&);
Uniqueptr & operator = (Uniqueptr<T> cosnt &)
private:
T * _ptr;
}
int main ()
{
//shared_ptr通过引用计数支持智能指针对象的拷贝
shared_ptr<Date> sp(new Date);
shared_ptr<Date> copy(sp);
cout << "ref count:" <<sp.use_count() << endl;
cout << "ref count:" << copy.use_count() << endl;
return 0;
}
//结果为2
shared_ptr的原理: 通过引用计数的方式来实现多个shared_ptr对象之间共享资源
- shared_ptr在内部,给每隔资源都维护着一份计数,用来记录该份资源被几个对象共享
- 在对象被销毁时(调用析构函数时),说明不在使用该资源,对象的引用-1
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
- 如果不是0,说明还有其他对象使用该资源,不能释放,否则其他对象称为野指针.
//实现简单shared_ptr
#pragma once
#include <thread>
#include <mutex>
using std::mutex;
template <class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = nullptr)
: _ptr(ptr)
, _pRefCount(new int(1))
, _pMutex(new mutex)
{}
~SharedPtr()
{
Release();
}
SharedPtr(const SharedPtr<T>& sp)
: _ptr(sp._ptr)
, _pRefCount(sp._pRefCount)
, _pMutex(sp._pMutex)
{
AddRefConut();
}
void AddRefConut()
{
//加锁或者使用加1的原子操作
_pMutex->lock();
(*_pRefCount);
_pMutex->unlock();
}
//sp1 = sp2
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
//if(this != &sp)
if (_ptr != sp._ptr)
{
//释放管理的旧资源
Release();
//共享管理新资源的对象,并增加引用计数
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pMutex = sp._pMutex;
AddRefConut();
}
return *this;
}
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
int UseCount() { return *_pRefCount; }
T* Get() { return _ptr; }
private:
void Release()
{
bool deleteflag = false;
//引用计数减一.如果减值0 释放资源
_pMutex->lock();
if ((*_pRefCount == 0))
{
delete _ptr;
delete _pRefCount;
deleteflag = true;
}
_pMutex->unlock();
if (deleteflag == true)
{
delete _pMutex;
}
}
private:
mutex *_pMutex; //互斥锁
int *_pRefCount; //引用计数
T* _ptr;//指向管理资源的指针
};
int main()
{
SharedPtr<int> sp1(new int(10));
SharedPtr<int> sp2(sp1);
*sp2 = 20;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
SharedPtr<int> sp3(new int(10));
sp2 = sp3;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
cout << sp3.UseCount() << endl;
sp1 = sp3;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
cout << sp3.UseCount() << endl;
return 0;
}
std::shared_ptr的线程安全
- 智能指针对象中引用计数是多个智能指针共享的,两个线程是智能指针的引用计数同时++或–, 这个操作不是原子的,引用计数原来是1, ++ 了两次,可能还是2. 此时引用计数错乱,会导致资源未释放或者程序崩溃,所以智能指针中引用计数++,–是需要加锁的,即引用计数的操作是线程安全的
- 智能指针管理的对象存放在堆上,两个线程同时去访问,可能会导致线程安全问题.
std::shared_ptr的循环引用
struct ListNode
{
int _data;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
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->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
循环引用分析:
- node1和node2两个智能指针对象指向两个节点,引用计数变成1,不需要手动delete
- node1的_next指向node2,node2的_prev指向node1,引用计数变为2
- node1和node2析构,引用计数减到1,但是_next还指向下一个节点,但是_prev还指向上一个节点
- _next析构了,node2释放,_prev析构,node1析构
- 但是与此同时,_next属于node1成员, node1释放了,_next才会析构,而node1由_prev管理._prev又属于node2成员
解决方法:
//在引用计数场景下,把节点中的_prev和_next改为weak_ptr
//此时,当node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和_prev不会增加
node1和node2的引用计数。
struct ListNode
{
int _data;
weak_ptr < ListNode> _prev;
weak_ptr < ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
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->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
内容总结
以上是互联网集市为您收集整理的C++智能指针全部内容,希望文章能够帮你解决C++智能指针所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。