智能指针
本文主要学习内容:
![在这里插入图片描述](https://img-blog.csdnimg.cn/352991eaa5c849b1849afe41027e2a13.png)
指针在C/C++中的重要性
指针用于访问程序外部的资源,例如堆内存。因此,如果在堆内存中创建了任何东西,则可以用指针访问堆内存。
普通指针出现的问题
我们先通过此例来了解:普通指针面对的主要问题是什么:
#include<iostream>
using namespace std;
class Rectangle
{
private:
int length;
int breadth;
}
void fun()
{
// By taking a pointer p and dynamically creating object of class rectangle
Rectangle* p = new Rectangle();
}
int main()
{
// Infinite loop
while(1)
{
fun();
}
}
💚 上述代码:
- 创建一个指针"p" ,指向一个矩形类型的对象,该对象具有长度和宽度。 一旦函数
- fun结束,将删除该“p”,因为p是函数的局部变量,该变量将终止,但是在堆内部分配的新矩形不会被释放,更不会被删除。
- 因为每次创建的对象,都不会被删除,因此它可能会导致内存泄漏,如果一直循环下去,在某一阶段,由于缺少堆内存,程序将崩溃 因此,
- 在fun() 的最后,如果我们不提及这一点 “delete p” , 这将会导致非常严重的问题
- 但是由于程序员的粗心,这种类型的问题是会出现的,因此,为了帮助程序员C++11承担责任并引入了 智能指针。
为什么引入智能指针
- 上面我们介绍了堆内存的问题在于,当不需要它时,必须将自身释放,否则就会出现内存泄漏,这将可能导致程序无内存可用,最终引发崩溃。
- 因此,诸如 java , C#, .Net Framework 之类的语言提供了垃圾回收机制来取消分配但未使用的对象。
- 那么对于C++ ,它在 C++11 中,引入了自动管理内存的智能指针,当指针不在作用域内时,他们将在不使用时自动释放对象,从而自动释放内存。
- 使用智能指针,我们可以使指针以不需要显式调用 delete的方式工作,智能指针是指针的包装类, 带有 *和-> 重载运算符,智能指针类的对象看起来像一个指针,但是可以执行普通指针不喜欢自动销毁的事情 。
- 💚
- 总数所述,智能指针是一个类,这个类中带有 :构造函数,析构函数,重载运算符(如:->和),那么当这个类的生命周期超出其作用域时,就会自动调用析构函数,而析构函数里面一般又会做释放对象的操作,这样就达到释放内存的目的。*
那么智能指针如何自动销毁对象了 ?
- 设计一个类,这个类带有指针,析构函数和重载运算符(例如: *和->) ,由于当对象超出范围时会自动调用析构函数,因此动态分配的内存将自动删除(或 可以减少引用计数)引用计数的对象一旦为零,那么就会自动调用析构函数,这个时候动态分配的内存也会被删除。
- 💚看下面例子:一个简单的智能指针类 ptr
#include<iostream>
using namespace std;
class SmartPtr
{
int* ptr; // Actual pointer
public:
// for use of explicit keyword
explicit SmartPtr(int* p = NULL) {
ptr = p;
}
// Destructor function
~SmartPtr(){
delete(ptr);
}
// overloading dereferencing operator
int& operator*()
{
return *ptr;
}
}
int main(){
// 构造SmartPtr对象 ptr
SmartPtr ptr(new int());
// 重载了 operatpr*() 操作符
*ptr = 20;
cout << *ptr;
return 0;
}
💚在编写一个:适用于所有类型的智能指针。
是的,我们可以用模板编写通用的智能指针类,以下是C++ 代码演示了相同的过程。
智能指针的类型
1:SharedPtr 带引用计数的智能指针
这种智能指针的应用是最广泛的。
tmplate<class T>
class SharedPtr
{
public:
SharedPtr(T* Ptr)
:_ptr(ptr)
,_pCount(new long(1))
{}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
:_pCount(sp._pCount)
{
++(*_pCount);
}
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
if (this != &sp){
_pCount = sp._pCount;
_ptr = sp._ptr;
++(*_pCount);
}
return *this;
}
~SharedPtr()
{
if(--(*_pCount) ==0)
{
_del(_ptr);
delete _pCount;
}
}
protected:
T* _ptr;
long* _pCount;
}
智能指针原理
- 智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count)。
- 智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。
💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚💚
手写智能指针
v0.1版本
#include<iostream>
using namespace std;
template<class T>
class Smart_ptr
{
public:
Smart_ptr(T* ptr = nullptr)
{
mptr = ptr;
}
~Smart_ptr(){
delete mptr;
}
private:
T* mptr;
}
int main(){
// 以前的写法
// int* p = new int;
// delete p;
// 现在替换成下面智能指针的方式
Smart_ptr<int> sp(new int);
return 0;
}
- 这个在栈上构造的对象sp 一旦走出作用域就会被自动析构掉,析构函数会删掉指针,那么指针也就释放呢。
v0.2 给智能指针增加一点功能
重载操作符(* 和 ->)让其具备裸指针的一些功能
#include<iostream>
using namespace std;
template<class T>
class Smart_ptr
{
public:
Smart_ptr(T* ptr = nullptr)
{
mptr = ptr;
}
~Smart_ptr(){
delete mptr;
}
T& operator*(){
return *(this->mptr);
}
T* operator->(){
return this->mptr;
}
private:
T* mptr;
}
int main(){
Smart_ptr<int> sp(new int);
*sp = 20;
cout << *sp << endl;
return 0;
}
v0.3 不带引用计数的指针
在版本 v0.2中,上面代码是不带引用计数的,那么它会暴露如下问题
int main()
{
Smart_ptr<int> sp(new int);
Smart_ptr<int> ps(sp);
return 0;
}
很显然在V0.2b版本中,如果出现上面情况,就会发生崩溃,这是为什么了?
因为在出作用域后,ps 先先析构,把资源释放了,等到 sp 再次析构的时候,就有资源释放了。
💚💚💚
解决方案
- 我们 在析构的时候,将指针置空,有用吗?答案并没有作用
~Smart_ptr(){
delete mptr;
mptr = nullptr;
}
- 首先我们要明白的是:这个问题出现的原因是浅拷贝带来的,因为浅拷贝并不会将资源拷贝到另一块内存区域。
- 那么我们写一个深拷贝了 ?把资源拷贝到另一块区域,当析构的时候,你走你的,我走我的,这样是可以的
- 但是这就违背了我们的初衷,我们希望裸指针能做的,智能指针都需要实现。
v0.4 智能指针升级版
带引用计数的智能指针:shared_ptr和 weak_ptr,可以使用多个智能指针管理同一个资源,实现方式就是:给每一个对象资源匹配一个引用计数。
#include<iostream>
using namespace std;
template<class TT>
class Refcnt
{
public:
Refcnt(TT* ptr = nullptr)
{
mptr = ptr;
if(mptr) {mcount = 1;}
}
void addRef()
{
mcount++;
}
int delRef()
{
return --mcount;
}
private:
TT* mptr;
int mcount;
}
template<class T>
class smart_ptr
{
smart_ptr(T* ptr = nullptr)
{
mptr = ptr;
mRefCnt = new Refcnt<T>(mptr);
}
~smart_ptr()
{
if(0 ==mRefcnt->delRef())
{
delete ptr;
mptr = nullptr;
}
}
T& operator*(){
return *(this->mptr);
}
T* operator->(){
return this->mptr;
}
smart_ptr<T>& operator=(smart_ptr<T> ptr)
{
if(this == &ptr)
{
return *this;
}
if(0==mRefcnt->delRef())
{
delete mptr;
}
mptr = ptr.mptr;
mRefcnt = ptr->mRefcnt;
mRefcnt->addRef();
return *this;
}
private:
T* mptr; // 指向资源
Refcnt<T>* mRefcnt;// 指向引用计数
}
int main()
{
smart_ptr<int> sp(new int);
smart_ptr<int> ps(sp);
return 0;
}
v0.5 智能指针循环引用问题
在v0.4版本中,使用 shared_ptr 这个称为强指针,强指针有个很严重的问题就是:循环引用或者交叉引用。