C++:指针:智能指针(一):智能指针及其类型,手写智能指针


本文主要学习内容:
在这里插入图片描述

指针在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();
	}
}

💚 上述代码:

  1. 创建一个指针"p" ,指向一个矩形类型的对象,该对象具有长度和宽度。 一旦函数
  2. fun结束,将删除该“p”,因为p是函数的局部变量,该变量将终止,但是在堆内部分配的新矩形不会被释放,更不会被删除。
  3. 因为每次创建的对象,都不会被删除,因此它可能会导致内存泄漏,如果一直循环下去,在某一阶段,由于缺少堆内存,程序将崩溃 因此,
  4. 在fun() 的最后,如果我们不提及这一点 “delete p” , 这将会导致非常严重的问题
  5. 但是由于程序员的粗心,这种类型的问题是会出现的,因此,为了帮助程序员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 再次析构的时候,就有资源释放了。
💚💚💚
解决方案

  1. 我们 在析构的时候,将指针置空,有用吗?答案并没有作用
~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 这个称为强指针,强指针有个很严重的问题就是:循环引用或者交叉引用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值