智能指针

** 什么是智能指针,为什么要有智能指针?**

  1. 智能指针是一个类,它封装了一个原始的C++指针,以管理所指对象的生命期。没有单一的智能指针类型,但所有这些都尝试以实用的方式抽象原始指针。
    智能指针应优先于原始指针。 如果你觉得你需要使用指针(首先要考虑你是否真的需要指针),你通常会想要使用智能指针,因为这可以缓解原始指针的许多问题,主要是忘记删除对象和泄漏内存。
    如果使用原始指针,程序员必须在指针不再有用时显式地销毁该对象
int *ptr = new int;
...
delete ptr;

下面这种情况不需要


int A = 10;
int *ptr = &A;    //因为这些是在栈上申请的,因为函数是在栈上进行展开,出了作用域还会自动释放
  1. 智能指针最重要的一个作用就是防止内存泄漏,(什么是内存泄漏?申请的空间没有释放。内存泄漏是指针丢了还是内存丢了?指针,通过指针可以找到这块内存,然后可以进行释放)
    所以,普通指针丢了会导致内存泄露的问题。
    另外一个作用就是解决异常安全问题,异常导致执行流乱跳,可能就会直接忽略释放动态内存的代码,这些可能就会造成内存泄漏。

智能指针说白了,就是用类封装出一个指针,这个指针出了作用域会自动调用析构释放资源,但平时开辟资源时,都是有一个指针指向这块空间的,有时对这块空间可能没有进行释放,或者释放时,空间还在使用,前者就有可能造成内存泄漏,但如果此时是智能指针的话,就不会有这种情况。

智能制指针的特性:

1.RAII(在初始化的时候把构造函数请求的资源保存起来,调用完毕之后,释放资源)```

2.本身它是对象,但是要能像指针一样--------所以要重载operator、->*
3.能赋值能拷贝
赋值(=),拷贝它既然是个类,能实例化出对象,那么对象1 = 对象2可否,此时这里是拷贝构造,因为没有=重载的成员函数

智能指针这个类和其他类最大的不同:
在这里插入图片描述

智能指针的发展--------解决它存在的潜拷贝问题

发展:c++98-----boost-----c++11(一代版本解决一代版本中的缺陷问题)
1 ..auto_ptr(c++98) --------------管理权转移,管理权转移的原因就是,赋值,拷贝的时候都是浅拷贝,浅拷贝析构两次出现问题
存在的问题:ap1的管理权 转移了,指向空,所以引用它的时候就问题了,禁止使用

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}

	// ap2(ap1)
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}

	// ap1 = ap3;
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			delete _ptr;          //释放原来的指向
			_ptr = ap._ptr;     //交换管理权,交付指向
			ap._ptr = NULL;   //指向置空
		}
		return *this;
	}

	~AutoPtr()
	{
		delete _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
protected:
	T* _ptr;
};
  1. scoped_ptr(boost版本) --------------防拷贝,说白了,就是不让拷贝,既然智能指针存在这个拷贝的问题,那就不然拷贝了,只声明,不定义,就可以通过编译,防拷贝还是为了解决只智能指针拷贝,赋值时出现的潜拷贝的问题。对这两个成员函数只是声明,不定义,那么就不会出现拷贝的时候默认浅拷贝的问题了,
    公司推荐使用 公司推荐使用
template<class T>
class ScopedPtr 
{
public:
	ScopedPtr(T* ptr)     //构造的时候把资源保存下来   
		:_ptr(ptr)
	{}

	~ScopedPtr()         //析构,清理资源,满足RAII
	{
		delete _ptr;
	}

	T& operator*()        //像指针一样
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

// 类的防拷贝,不让一个类拷贝,只声明,不实现,如果不声明,就会使用默认的拷贝---浅拷贝
private:  //给成私有,防止外部拿走后自行实现
	ScopedPtr(const ScopedPtr<T>& ap);      //拷贝构造函数
	ScopedPtr<T>& operator=(const ScopedPtr<T>& ap);   //赋值运算符重载
protected:
	T* _ptr;
};
  1. shared_ptr(boost版本)
    scoped_ptr,面试介意可以实现,但是在实际中scoped_ptr不让拷贝,但有时就是需要拷贝,赋值,所以就出现了shared_ptr,
    它的核心就是引用计数,但是存在循环引用问题和线程安全问题,所以出现了weak_ptr----解决循环引用问题,辅助shared_ptr。

在这里插入图片描述

在这里插入图片描述

struct ListNode                   
{
	Shared_ptr<ListNode> _next;      //节点类型为Shared_ptr;
    Shared_ptr<ListNode> _prev;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};
template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = NULL)
		:_ptr(ptr)
		, _pCount(new int(1))      //一创建就给加1                  
	{}

	~SharedPtr()
	{
		cout << "~SharedPtr()" << endl;
		if (--(*_pCount) == 0)           //只有一个还在管理的时候,才进行释放
		{
			delete _ptr;
			delete _pCount;
		}
	}

	T& operator*()
	{
		return *_ptr;      //解引用返回的是对象
	}

	T* operator->()
	{
		return _ptr;
	}

	// sp2(sp1)
	SharedPtr(SharedPtr<T>& sp)             //拷贝构造函数
		:_ptr(sp._ptr)
		, _pCount(sp._pCount)
	{
		++(*_pCount);
	}

	// sp1 = sp2
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)       //赋值 运算符重载,注意里面的坑 
	{
		//if (this != &sp),这个不好,用下面的判断,
		if (_ptr != sp._ptr)    //指针不相等,说明不是同一个对象,不是自己被自己赋值
		{
			if (--(*_pCount) == 0)   //有可能之前,s1已经有所指向了,即sp1,sp2之前分别指向两块空间
			{                        //所以先清空s1
				delete _ptr;
				delete _pCount;
			}

			_ptr = sp._ptr;
			++(*sp._pCount);
			_pCount = sp._pCount;
		}

		return *this;
	}
protected:
	T* _ptr;
	int* _pCount;        //引用计数
};
int main()
{
SharedPtr<ListNode> n1 = new ListNode;                //new 一个ListNode 类型的对象交给智能指针对象管理
SharedPtr<ListNode> n2 = new ListNode;             
n1->_next = n2;           //调用重载的=,使得cout++ = 2
n2->_prev = n1;         
}

shared_ptr的循环引用,什么是循环引用?

以双向列表为例,

SharedPtr<ListNode> n1 = new ListNode;                //new 一个ListNode 类型的对象交给智能指针对象管理
SharedPtr<ListNode> n2 = new ListNode;             
n1->_next = n2;           //调用重载的
n2->_prev = n1;           

在这里插入图片描述
n1,n2出了作用域后就释放,

在这里插入图片描述
**shared_ptr的线程安全问题 **

在这里插入图片描述
多线程问题就是对于多核来说,多个进程可以在同一时间同时访问某些资源,所以两个线程可能都取到2这个引用计数,然后就都减减到1,就都没有释放,造成资源泄漏。可以考虑给引用计数加锁
4. weak_ptr(boost版本)
解决循环引用的问题,如何解决的?
weak_ptr不增加shared_ptr的引用计数,保存的是shared_ptr的资源
在这里插入图片描述
在这里插入图片描述

struct ListNode        //双向链表的类型是weak_ptr
{
	WeakPtr<ListNode> _next;
	WeakPtr<ListNode> _prev;

	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};


template<class T>
class shared_Ptr
{
	template<class>
	friend class WeakPtr;   
public:
	//RAII
	shared_Ptr(T *p)
		:ptr(p)
	{}
	~shared_Ptr()
	{
		delete ptr;
	}
	//像指针一样
	T &operator*(shared_Ptr<T> &s)
	{
		return *ptr;
	}
	shared_Ptr<T>&operator=(shared_Ptr<T> &s)
	{
		return ptr;
	}
	//拷贝str1(str2)
	shared_Ptr(shared_Ptr<T> &s)
		:ptr(s.ptr)
		,count(s.count)
	{
		++(*count);
	}
	//str1=str2
  shared_Ptr<T>& operator=(shared_Ptr<T> &s)
	{
		if (ptr != s.ptr)
		{
			if (--(*count) == 0)    //如果之前有指向则结果减1
			{
				delete ptr;    //清空之前所指的内容
				delete count;
			}
			ptr = s.ptr;
			++(*s.count);    //为什么
			count = s.count;
		}
		return *this;

	}

private:
	T* ptr;
	T *count;
};

template<class T>
class weak_ptr        
{
	
public:
	WeakPtr()
		:_ptr(NULL)
	WeakPtr(SharedPtr<T>& sp)     //拷贝构造跟其他不同,
		:_ptr()
	{}
	WeakPtr<T>& operator=(SharedPtr<T>& sp)   //关注重点
	{
		_ptr = sp._ptr;
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T *operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;

};
int main()
{
SharedPtr<ListNode> n1 = new ListNode;
SharedPtr<ListNode> n2 = new ListNode;

	n1->_next = n2;     
	n2->_prev = n1;

	ListNode* p = new ListNode;
	delete p;
}
  1. c++11的智能指针
    unique_ptr—相当于scoped_ptr
    shared_ptr
    weak_ptr
  2. 查看一下库里的智能指针的使用方法,尤其是看重载[]
struct ListNode
{
weak_ptr<ListNode> _next;
weak_ptr<ListNode> _prev;
}
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
node1->_next = node2;
node2->_prev = node1;
cout<<node1.use_cout()<<endl;


仿函数

面试问的少
像函数,但它是一个对象,它调用的是operator();,这个重载的()就叫做防函数

struct Less
{
  bool operator()(const T& x1,const T&x2)
  {
        return x1<x2;
  }
}
Less<int> less;
cout<<less(1,2)<<endl;           //仿函数

所以,当一个类中重载了(),这个类就是仿函数类

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值