c++之智能指针

1.什么是RAII
RAII是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等等)的简单技术。在对象构造时获取资源,接着控制对象的访问是指在对象的生命周期内始终保持有效,最后在对象析构时释放资源,借此,我们实际上是把管理一份资源的责任托管给了一个对象,这种做法有两大好处:

  • 不需要显式的释放资源
  • 采用这种方式,对象所需的资源在其生命期呃逆始终保持有效

2.智能指针
2.1什么是智能指针?
所谓的智能指针就是智能/自动化的管理指针所指向的动态资源的释放,智能指针是一个类,有类似指针的功能

2.2为什么需要智能指针?
c++程序设计中使用堆内存是非常频繁的操作,堆内存的申请与释放都是有程序员自己管理的,程序员自己管理堆内存可以提高程序的效率,但是整体来说堆内存的管理是非常麻烦的,c++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄漏(忘记释放)二次释放,程序发生异常时内存泄漏等问题,使用智能指针能更好地管理堆内存。因为智能指针就是一个类,当超过类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。

2.3.智能指针实现原理
实现原理:通过RAII方式将原生态管理起来–构造函数中放资源,析 构函数中将资源释放掉-----用户不需要关心什么时机释放资源,让该类对象具有像指针一样的行为:T&operstor*() T*operator->()
缺陷:具有浅拷贝问题

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 A {
 int a;
 int b;
 int c;
};
void TestFunc() {
 smartptr<int>sp1(new int);
 *sp1 = 1;
 smartptr<A>sp2(new A);
 sp2->a = 2;
 sp2->b = 3;
 sp2->c = 4;
}
int main() {
 TestFunc();
 return 0;
}

不同类型的智能指针按照上述方式实现后,只需要解决浅拷贝的问题即可
c++98:auto_ptr–>RAII+operator*()/operator->()+资源管理权转移 auto_ptrap1(new int)
auto_ptrap2(ap1)
原理:管理权转移思想
后果:ap1将不再操作资源 ap1与资源断开联系

namespace bite {
 //1:通过RAII的方式管理指针
 //2:具有像指针的行为:解引用,->
 //3:解决浅拷贝问题(深拷贝(不行))原因:资源是外部用户提供的,类只负责管理资源,没有申请资源的权利
 //c++98 auto_ptr:资源转移
 template<class T>
 class auto_ptr {
 public:
  auto_ptr(T*ptr=nullptr)
   :_ptr(ptr)
  {}
  auto_ptr(auto_ptr<T>& ap) {
   _ptr = ap._ptr;
   ap._ptr = nullptr;
  }
  auto_ptr<T>& operator=(auto_ptr<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;
 };
}
void TestFunc() {
 bite::auto_ptr<int>ap1(new int);
 bite::auto_ptr<int>ap2(ap1);
 ap1 = ap2;
}
int main() {
 TestFunc();
 return 0;
}

c++03:ap1将其堆资源释放的权利交给ap2来进行管理后果:可能会产生野指针

namespace bite {
 template<class T>
 class auto_ptr {
 public:
  auto_ptr(T* ptr = nullptr)
   :_ptr(ptr)
   , _owner(false)
  {
   if (_ptr)
    _owner = true;
  }
  auto_ptr(const auto_ptr<T>& ap)
   :_ptr(ap._ptr)
   , _owner(ap._owner)
  {
   ap._owner = false;
  }
  auto_ptr<T>&operator= (const auto_ptr<T>& ap) {
   if (this != &ap) {
    Release();
    _ptr = ap._ptr;
    _owner = ap._owner;
    ap._owner = false;
   }
   return *this;
  }
  ~auto_ptr() {
   Release();
  }
  T& operator*() {
   return *_ptr;
  }
  T* operator->() {
   return _ptr;
  }
 private:
  void Release() {
   if (_ptr && _owner) {
    delete _ptr;
   }
  }
 private:
  T* _ptr;
  mutable bool _owner;
 };
}
void TestFunc() {
 bite::auto_ptr<int>ap1(new int);
 bite::auto_ptr<int>ap2(ap1);
 ap1 = ap2;
 if (true) {
  bite::auto_ptr<int>ap3(ap2);
 }
 *ap1 = 10;
 *ap2 = 20;
 //ap1和ap2为野指针,会造成程序崩溃
}
int main() {
 TestFunc();
 return 0;
}

建议:什么情况下都不要使用auto_ptr;
c++11:unique_ptr:RAII+operator*()/operator->()+独立管理资源,不能共享资源(不能进行拷贝)
c++98:拷贝构造函数与赋值运算符重载只声明不定义&权限为private c++11:拷贝构造函数与赋值运算符重载=delete原理:简单粗暴的防拷贝缺陷:对象之间不能共享资源

//unique_ptr:一份资源只能让一个对象管理,不允许共享
//不能被拷贝
namespace bite {
 template<class T>
 class unique_ptr {
 public:
  unique_ptr(T*ptr=nullptr)
   :_ptr(ptr)
  {}
  ~unique_ptr() {
   if (_ptr)
    delete _ptr;
  }
  T& operator*() {
   return *_ptr;
  }
  T* operator->() {
   return _ptr;
  }
 // //c++98
 //private:
 // unique_ptr(const unique_ptr<int>& up);
 // unique_ptr<T>& operator=(const unique_ptr<int>& up);
 //c++11
  unique_ptr(const unique_ptr<int>& up) = delete;
  unique_ptr<int>& operator=(const unique_ptr<int>& up) = delete;
 private:
  T* _ptr;
 };
}
void TestFunc() {
 bite::unique_ptr<int>up1(new int);
 bite::unique_ptr<int>up2(up1);
 up1 = up2;
}
int main() {
 TestFunc();
 return 0;
}

shared_ptr: RAII+operator*()/operator->()+引用计数==共享
原理:通过引用计数的方式来实现多个shared_ptr对象之间的共享资源

  • shared_ptr在其内部,给每个资源都维护着一份计数,用来记录该份资源被几个对象共享
  • 在对象被销毁时(也就是析构函数调用),就说明自己不用该资源了,对象的引用计数减一。
  • 如果引用计数是0,就说明自己是最后一个使用该资源的对象们必须释放资源
  • 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放资源,否则其他对象就成野指针了
namespace bite {
 template<class T>
 class shared_ptr {
 public:
  shared_ptr(T*ptr=nullptr)
   :_ptr(ptr)
   ,_pCount(nullptr)
  {
   if (_ptr) {
    _pCount = new int(1);
   }
  }
  ~shared_ptr() {
   Release();
  }
  shared_ptr(const shared_ptr<T>& sp)
   :_ptr(sp._ptr)
   , _pCount(sp._pCount)
  {
   if (_ptr) {
    ++(*_pCount);
   }
  }
  //sp1:未管理资源--直接与sp2共享
  //sp1:单独管理资源--在与sp2共享前,必须先释放自己的资源
  //sp1:与其他shared_ptr的对象已经共享资源--sp1将其计数减1
  shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
   if (_ptr!=sp._ptr) {
    Release();
    //与sp共享
    _ptr = sp._ptr;
    _pCount = sp._pCount;
    if (_ptr) {
     ++(*_pCount);
    }
   }
   return *this;
  }
  T& operator*() {
   return *_ptr;
  }
  T* operator->() {
   return _ptr;
  }
  int usecount() {
   return *_pCount;
  }
 private:
  void Release() {
   if (_ptr && 0 == (-- * _pCount)) {
    delete _ptr;
    delete _pCount;
   }
  }
 private:
  T* _ptr;
  int* _pCount;
 };
}
void TestFunc() {
 bite::shared_ptr<int>sp1(new int);
 bite::shared_ptr<int>sp2(sp1);
 bite::shared_ptr<int>sp3(new int);
 bite::shared_ptr<int>sp4;
 sp4 = sp1;//sp4未管理资源
 sp3 = sp1;//sp3单独管理一份资源--释放旧资源,与sp1共享
 sp1 = sp2;
}
template<class T>
class DFDel {
public:
 void operator()(T*& p) {
  if (p) {
   delete p;
   p = nullptr;
  }
 }
};
template<class T>
class Free {
public:
 void operator()(T*& p) {
  if (p) {
   free(p);
   p = nullptr;
  }
 }
};
class FClose {
public:
 void operator()(FILE* p) {
  if (p) {
   fclose(p);
   p = nullptr;
  }
 }
};
namespace bite {
 template<class T,class DF=DFDel<T>>
 class shared_ptr {
 public:
  shared_ptr(T* ptr = nullptr)
   :_ptr(ptr)
   , _pCount(nullptr)
  {
   if (_ptr) {
    _pCount = new int(1);
   }
  }
  ~shared_ptr() {
   Release();
  }
  shared_ptr(const shared_ptr<T>& sp)
   :_ptr(sp._ptr)
   , _pCount(sp._pCount)
  {
   if (_ptr) {
    ++(*_pCount);
   }
  }
  shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
   if (_ptr != sp._ptr) {
    Release();
    //与sp共享
    _ptr = sp._ptr;
    _pCount = sp._pCount;
    if (_ptr) {
     ++(*_pCount);
    }
   }
   return *this;
  }
  T& operator*() {
   return *_ptr;
  }
  T* operator->() {
   return _ptr;
  }
  int usecount() {
   return *_pCount;
  }
 private:
  void Release() {
   if (_ptr && 0 == (-- * _pCount)) {
    //DF df;
    //df(_ptr);
    //等价于
    DF()(_ptr);//DF是一个类型  DF():创建一个无名对象
    delete _pCount;
   }
  }
 private:
  T* _ptr;
  int* _pCount;
 };
}
void TestFunc() {
 bite::shared_ptr<int,Free<int>>sp1((int*)malloc(sizeof(int)));
 bite::shared_ptr<FILE,FClose>sp2(fopen("666.txt", "rb"));
 bite::shared_ptr<int>sp3(new int);
}
int main() {
 TestFunc();
 return 0;
}

缺陷:循环引用

//shared_ptr:循环引用问题
#include<memory>
struct ListNode {
 ListNode(int data=int())
  :_pre(nullptr)
  ,_next(nullptr)
  ,_data(data)
 {
  cout << "ListNode():" << this << endl;
 }
 ~ListNode() {
  cout << "~ListNode():" << this << endl;
 }
 shared_ptr<ListNode>_pre;
 shared_ptr<ListNode>_next;
 int _data;
};
void TestFunc() {
 shared_ptr<ListNode>sp1(new ListNode(10));
 shared_ptr<ListNode>sp2(new ListNode(20));
 cout << sp1.use_count() << endl;
 cout << sp2.use_count() << endl;
 sp1->_next = sp2;
 sp2->_next = sp1;
 cout << sp1.use_count() << endl;
 cout << sp2.use_count() << endl;
}
int main() {
 TestFunc();
 return 0;
}

循环引用分析:

  • sp1和sp2两个智能指针对象指向两个结点,引用计数变为1,我们不需要手动delete
  • sp1的next指向sp2,sp2的pre指向sp1,引用计数变为2
  • sp1和sp2析构,引用计数减到1,但是_next还指向下一个节点,_pre还指向上一个节点
  • 也就是说,_next析构了,sp1就释放了
  • 也就是说,_pre析构了,sp2就释放了
  • 但是_next属于sp1的成员,sp1释放了,_next才会析构,而sp1有_pre管理,_pre属于sp2成员,所以这就叫循环引用,谁也不会释放

解决方案

//shared_ptr:循环引用问题
//weak_ptr:不能单独管理资源,必须配合shared_ptr一起使用
//         因为weak_ptr作用:就是为了解决shared_ptr存在的循环引用问题而生的
#include<memory>
struct ListNode {
 ListNode(int data=int())
 :_data(data)
 {
  cout << "ListNode():" << this << endl;
 }
 ~ListNode() {
  cout << "~ListNode():" << this << endl;
 }
 weak_ptr<ListNode>_pre;
 weak_ptr<ListNode>_next;
 int _data;
};
void TestFunc() {
 shared_ptr<ListNode>sp1(new ListNode(10));
 shared_ptr<ListNode>sp2(new ListNode(20));
 cout << sp1.use_count() << endl;
 cout << sp2.use_count() << endl;
 sp1->_next = sp2;
 sp2->_next = sp1;
 cout << sp1.use_count() << endl;
 cout << sp2.use_count() << endl;
}
int main() {
 //编译报错:weak_ptr不能单独管理资源
 //weak_ptr<int>wp(new int);
 TestFunc();
 return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值