【C++】智能指针详解

智能指针:将对内存的管理交付给对象,当对象析构时就能够清理资源,有效的避免内存泄露问题。(因为原生态指针(T*)管理资源时,若用户忘记释放内存,则可能会导致资源泄露。)

  • 下面介绍四种智能指针
  • 头文件均为#include< memory>

一.auto_ptr智能指针

  • C++98中经历过两次版本更新。
  • C++11中保留第一版auto_ptr。

1.首先介绍智能指针的三大结构

  • 1.RAII:利用对象生命周期来控制内存资源,构造时获取资源,析构时释放资源。
    特点:不需要显式释放资源。对象所需资源在其生命周期内始终有效。
  • 2.重载运算符 * (解引用) ->
  • 3.拷贝构造方法/重载=运算符方法,所产生的浅拷贝问题。

2.auto_ptr第一版本模拟实现
解决浅拷贝:通过资源转移的方法,一份资源只能由一个指针管理。之前的指针设置为空。

#include<iostream>
using namespace std;
template <class T>
class auto_ptr1 {
public:
//1.RAII:
	auto_ptr1(T* ptr=nullptr)
		:_ptr(ptr)
	{}
	//2.重载* -> 运算符
	T& operator *() {
		return *_ptr;
	}
	T& operator ->() {
		return _ptr;
	}
	//3.拷贝构造  并用资源转移的方法解决浅拷贝
	auto_ptr1(auto_ptr1<T>& ap) 
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}
	auto_ptr1<T>& operator =(auto_ptr1<T>& ap) {
		if (this != &ap) {
			if (_ptr) {
				delete _ptr;
			}
			_ptr = ap._ptr;
			ap._ptr = nullptr;
		}
		_ptr = ap._ptr;
		ap._ptr = nullptr;
	}
	~auto_ptr1() {
		if (_ptr){
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
};
int main() {
	auto_ptr<int> ap1(new int);
	*ap1 = 10;
	auto_ptr<int> ap2(ap1);
	*ap2 = 20;
	auto_ptr<int> ap3(new int);
	*ap3 = 30;
	auto_ptr<int> ap4;
	ap4 = ap3;
	*ap4 = 40;
	return 0;
}

在这里插入图片描述
2.auto_ptr第二版本模拟实现
解决浅拷贝:通过资源管理权限的方法,设置一个bool类型变量,只有最后一个指针为true,拥有可以释放资源的权利。

#include<iostream>
#include<Windows.h>
using namespace std;
template <class T>
class auto_ptr1 {
public:
	auto_ptr1(T* ptr=nullptr)
		:_ptr(ptr)
		,_owner(false)
	{
		if (_ptr) {
			_owner = true;
		}
	}
	T& operator *() {
		return *_ptr;
	}
	T& operator ->() {
		return _ptr;
	}
	auto_ptr1(auto_ptr1<T>& ap) 
		:_ptr(ap._ptr)
		,_owner(ap._owner)
	{
		ap._owner = false;
	}
	auto_ptr1<T>& operator =(auto_ptr1<T>& ap) {
		if (this != &ap) {
			if (_ptr&&_owner) {
				delete _ptr;
			}
			_ptr = ap._ptr;
			_owner = ap._owner;
			ap._owner = false;
		}
		return *this;
	}
	~auto_ptr1() {
		if (_ptr&&_owner){
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
	bool _owner;
};
int main() {
	auto_ptr1<int> ap1(new int);
	*ap1 = 10;
	auto_ptr1<int> ap2(ap1);
	*ap2 = 20;
	auto_ptr1<int> ap3(new int);
	*ap3 = 30;
	auto_ptr1<int> ap4;
	ap4 = ap3;
	*ap4 = 40;
	return 0;
}

在这里插入图片描述
3.C++11中,auto_ptr恢复到第一版本,因为上一版本有缺陷(可能成为野指针)。并提供了更优质的智能指针。

二.unique_ptr智能指针

1.原理:

  • RAII
  • 重载* ->
  • 解决浅拷贝:不允许调用拷贝构造函数以及调用重载=运算符。即不允许进行拷贝。

2.unique_ptr模拟实现

#include<iostream>
using namespace std;
template <class T>
class unique_ptr1 {
public:
	unique_ptr1(T* ptr=nullptr)
		:_ptr(ptr)
	{}
	T& operator *() {
		return *_ptr;
	}
	T& operator ->() {
		return _ptr;
	}
	unique_ptr1(unique_ptr1<T>& ap)=delete   //函数后面添加=delete  表示函数被删除,不允许被调用。
	{}
	//private :
	//            unique_ptr1(unique_ptr1<T>& ap){}  将函数私有化也可以。
	unique_ptr1<T>& operator =(unique_ptr1<T>& ap) =delete
	{}  
	~unique_ptr1() {
		if (_ptr){
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
};
int main() {
	unique_ptr1<int> ap1(new int);
	*ap1 = 10;
	return 0;
}

三.shared_ptr智能指针

1.原理

  • RAII
  • 重载* ->
  • 解决浅拷贝:利用引用计数方式(记录资源被对象使用的个数)

2.代码

#include<iostream>
using namespace std;
template <class T>
class shared_ptr1 {
public:
	shared_ptr1(T* ptr=nullptr)
		:_ptr(ptr)
		,_pcount(new int)
	{
		if (_ptr)
			*_pcount = 1;
	}
	T& operator *() {
		return *_ptr;
	}
	T& operator ->() {
		return _ptr;
	}
	shared_ptr1(shared_ptr1<T>& ap)
		:_ptr(ap._ptr)
		, _pcount(ap._pcount)
	{
		if (_ptr)
			(*_pcount)++;
	}
	shared_ptr1<T>& operator =(shared_ptr1<T>& ap)
	{
		if (this->_ptr != ap._ptr) {
			if (_ptr&&--*_pcount == 0) {
				delete _ptr;
				_ptr = nullptr;
			}
			_ptr = ap._ptr;
			_pcount = ap._pcount;
			if (_ptr) {
				(*_pcount)++;
			}
	     }
		return *this;
	}
	~shared_ptr1() {
		if (_ptr&&--*_pcount==0){
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
	int * _pcount;
};
int main() {
	shared_ptr1<int> ap1(new int);
	*ap1 = 10;
	shared_ptr1<int> ap2(ap1);
	*ap2 = 20;
	shared_ptr1<int> ap3(new int);
	*ap3 = 30;
	shared_ptr1<int> ap4(new int);
	ap4 = ap3;
	*ap4 = 40;
	return 0;
}

在这里插入图片描述

四.weak_ptr智能指针

1.说明:weak_ptr与shared_ptr原理相同,均采用引用计数的方法解决浅拷贝。但weak_ptr不可以单独使用,必须配套shared_ptr,为了解决shared_ptr产生的循环引用(相互引用,互相等待,不能调用析构函数,从而资源泄露)。-------》只需将结构内部循环引用的智能指针替换为weak_ptr即可。
2.原理:shared_ptr与weak_ptr都有一个独立的_pcount引用计数。所以可以避免循环引用导致的资源泄露。
3.代码

#include<iostream>
#include<memory>
using namespace std;
struct ListNode
{
	int _data;
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;   //只需将结构内部循环引用的智能指针替换为weak_ptr即可。
	~ListNode() { cout << "~ListNode()" << endl; }
};
int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	node1->_next = node2;
	node2->_prev = node1;
	return 0;
}

五.智能指针注意事项

1.删除器:析构时,不同的构造方式需要调用不同的析构函数。

template<class T>
struct FreeFunc {
 void operator()(T* ptr)
 {
 free(ptr);
 }
};
template<class T>
struct DeleteArrayFunc {
 void operator()(T* ptr)
 { 
 delete[] ptr; 
 }
}
int main()
{
 FreeFunc<int> freeFunc;
 shared_ptr<int> sp1((int*)malloc(4), freeFunc);
 DeleteArrayFunc<int> deleteArrayFunc;
 shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc);
 return 0; 
 }

2.线程安全问题:当遇到多线程问题时,资源可能不安全。
解决方法:加锁以及解锁。

#include <thread>
#include <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)
 {
 AddRefCount();
 }
 // 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;
 AddRefCount();
 }
 return *this;
 }
 T& operator*() {return *_ptr;}
 T* operator->() {return _ptr;}
 int UseCount() {return *_pRefCount;}
 T* Get() { return _ptr; }
 void AddRefCount()
 {
 // 加锁或者使用加1的原子操作
 _pMutex->lock();
 ++(*_pRefCount);
 _pMutex->unlock();
 }
private:
 void Release()
 {
 bool deleteflag = false;
 // 引用计数减1,如果减到0,则释放资源
 _pMutex.lock();
 if (--(*_pRefCount) == 0)
 {
 delete _ptr;
 delete _pRefCount;
 deleteflag = true;
 }
 _pMutex.unlock();
 
 if(deleteflag == true)
 delete _pMutex;
 }
private:
 int* _pRefCount; // 引用计数
 T* _ptr; // 指向管理资源的指针 
 mutex* _pMutex; // 互斥锁
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针详解 智能指针详解 智能指针内容很多,重点是基本⽤法。 #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_from_this<CBase> { public: virtual void f(){}//必须有个虚函数才能向上向下转换。 } typedef boost::shared_ptr<CBase> CBasePtr; class CChild: public CBase {} typedef boost::shared_ptr<CChild> CChildPtr; void main() { CBasePtr ptrBase = boost::make_shared<CBase>(); //CBasePtr ptrBase = CBasePtr(new CBase()); // 向下转换 CChildPtr ptrChild = boost::dynamic_pointer_cast<CChild>(ptrBase); // 向上转换 CBasePtr ptrXXX = ptrChild; // 普通转换 CChildPtr ptrXX = CChildPtr(dynamic_cast<CChild*>(ptrXXX.get())); } 暂时学会这些⽤法即可。 url: C++ 智能指针详解 ⼀、简介 由于 C++ 语⾔没有⾃动内存回收机制,程序员每次 new 出来的内存都要⼿动 delete。程序员忘记 delete,流程太复杂,最终导致没 有 delete,异常导致程序过早退出,没有执⾏ delete 的情况并不罕见。 ⽤智能指针便可以有效缓解这类问题,本⽂主要讲解参见的智能指针的⽤法。包 括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、、boost::weak_ptr、boost::intrusive_ptr。你可能会想,如 此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇⽂章后,我想你⼼⾥⾃然会有答案。 下⾯就按照顺序讲解如上 7 种智能指针(smart_ptr)。 ⼆、具体使⽤ 1、总括 对于编译器来说,智能指针实际上是⼀个栈对象,并⾮指针类型,在栈对象⽣命期即将结束时,智能指针通过析构函数释放有它管理的 堆内存。所有智能指针都重载了"operator->"操作符,直接返回对象的引⽤,⽤以操作对象。访问智能指针原来的⽅法则使⽤"."操作符。 访问智能指针包含的裸指针则可以⽤ get() 函数。由于智能指针是⼀个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指 针是否为空,需要这样判断:if (my_smart_object.get())。 智能指针包含了 reset() ⽅法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递⼀个对象,则智能指 针会释放当前对象,来管理新传⼊的对象。 我们编写⼀个测试类来辅助分析: class Simple { public: Simple(int param = 0) { number = param; std::cout << "Simple: " << number << std::endl; } ~Simple() { std::cout << "~Simple: " << number << std::endl; } void PrintSomething() { std::cout << "PrintSomething: " << info_extend.c_str() << std::endl; } std::string info_extend; int number; }; 2、std::auto_ptr std::auto_ptr 属于 STL,当然在 namespace std 中,包含头⽂件 #include<memory> 便可以使⽤。std::auto_ptr 能够⽅便的管理单个堆 内存对象。 我们从代码开始分析: void TestAutoPtr() { std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1 if (my_memory.get()) { // 判断智能指针是否为空 my_memory->PrintSomething(); // 使⽤ operator-> 调⽤智能指针对象中的函数 my_memory.get()->info_extend = "Addition";
C++智能指针详解 1、概述 我们知道除了静态内存和栈内存外,每个程序还有⼀个内存池,这部分内存被称为⾃由空间或者堆。程序⽤堆来存储动态分配的对象即那些 在程序运⾏时分配的对象,当动态对象不再使⽤时,我们的代码必须显式的销毁它们。 在C++中,动态内存的管理是⽤⼀对运算符完成的:new和delete。 new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的指针; delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。 动态内存管理经常会出现两种问题: (1)⼀种是忘记释放内存,会造成内存泄漏; (2)⼀种是尚有指针引⽤内存的情况下就释放了它,就会产⽣引⽤⾮法内存的指针。 为了更加容易(更加安全)的使⽤动态内存,引⼊了智能指针的概念。智能指针的⾏为类似常规指针,重要的区别是它负责⾃动释放所指向 的对象。 标准库提供的两种智能指针的区别在于管理底层指针的⽅法不同:shared_ptr和unique_ptr。 (1)shared_ptr允许多个指针指向同⼀个对象; (2)unique_ptr则"独占"所指向的对象。 标准库还定义了⼀种名为weak_ptr的伴随类,它是⼀种弱引⽤,指向shared_ptr所管理的对象,这三种智能指针都定义在memory头⽂件 中。 2、auto_ptr (不要使⽤的指针) (1)内部⼤概实现:做成⼀个auto_ptr类,包含原始指针成员。 当auto_ptr类型的对象被释放时,利⽤析构函数,将拥有的原始指针delete掉。 //⼤概长这个样⼦(简化版) template<class T> class auto_ptr {   T* ptr; }; (2)⽰例⽤法: void runGame() { std::auto_ptr<Monster> monster1(new Monster());//monster1 指向 ⼀个怪物 monster1->doSomething();//怪物做某种事 }//runGame函数执⾏完时,monster1被释放,然后它的析构函数也把指向的⼀个怪物释放了 复制auto_ptr对象时,把指针指传给复制出来的对象,原有对象的指针成员随后重置为nullptr。 这说明auto_ptr是独占性的,不允许多个auto_ptr指向同⼀个资源。 void runGame() { std::auto_ptr<Monster> monster1(new Monster());//monster1 指向 ⼀个怪物 monster1->doSomething(); //怪物做某种事 std::auto_ptr<Monster> monster2 = monster1; //转移指针 monster2->doSomething(); //怪物做某种事 monster1->doSomething(); //Oops!monster1智能指针指向了nullptr,运⾏期崩溃。 } 注意: 虽然本⽂简单介绍了auto_ptr。 但是不要⽤auto_ptr! 不要⽤auto_ptr! 虽然它是c++11以前的最原始的智能指针,但是在c++11中已经被弃⽤(使⽤的话会被警告)了。 它的替代品,也就是c++11新智能指针unique_ptr,shared_ptr,weak_ptr。 3、shared_ptr(⼀种强引⽤指针) 多个shared_ptr指向同⼀处资源,当所有shared_ptr都全部释放时,该处资源才释放。 (有某个对象的所有权(访问权,⽣命控制权) 即是 强引⽤,所以shared_ptr是⼀种强引⽤型指针) (1)内部⼤概实现:每次复制,多⼀个共享同处资源的shared_ptr时,计数+1。每次释放shared_ptr时,计数-1。 当shared计数为0时,则证明所有指向同⼀处资源的shared_ptr们全都释放了,则随即释放该资源(哦,还会释放new出来的 SharedPtrControlBlock)。 //shared计数放在这个结构体⾥⾯,实际上结构体⾥还应该有另⼀个weak计数。下⽂介绍weak_ptr时会解释。 struct SharedPtrControlBlock {   int shared_count; }; //⼤概长这个样⼦(简化版) template<class T> class shared_ptr {   T* ptr;   SharedPtrControlBlock* count; }; (2)⽰例⽤法: void runGame() {   std::shared_ptr<Monster> monster1(new Monster());  //计数加到1  do{ std::shared_ptr<Monster> monster

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值