【c++】学习智能指针

本文详细介绍了C++中的智能指针概念,包括auto_ptr、unique_ptr、shared_ptr和weak_ptr,以及RAII技术的应用。通过实例演示了如何使用这些工具来自动管理内存,避免手动释放带来的问题,以及它们在生命周期控制和资源管理中的优势。
概念
智能指针

所谓的智能指针本质就是一个类模板,它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。

RAII

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

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。
四种智能指针
auto_ptr

auto_ptr 是通过由 new 表达式获得的对象,并在 auto_ptr 自身被销毁时删除该对象的智能指针。它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象。
复制 auto_ptr ,会复制指针并转移所有权给目标: auto_ptr 的复制构造和复制赋值都会修改其右侧参数,而且“副本”不等于原值。因为这些不常见的复制语义,不可将 auto_ptr 置于标准容器中
成员类型

  • 成员类型 定义
    element_type T

  • 成员函数
    (构造函数) —— 创建新的 auto_ptr
    (析构函数)——销毁 auto_ptr 和被管理对象
    operator= —— 从另一 auto_ptr 转移所有权
    operator auto_ptroperator auto_ptr_ref
    转换被管理指针到指向另一类型的指针
    get —返回指向被管理对象的指针
    operator*operator-> -------访问被管理对象
    reset-------替换被管理对象
    release --------释放被管理对象的所有权

unique_ptr

std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:
◦最后剩下的占有对象的 shared_ptr 被销毁;
◦最后剩下的占有对象的 shared_ptr 被通过 operator= 或 ◦reset() 赋值为另一指针
◦(构造函数) 构造新的 shared_ptr
◦(析构函数)如果没有更多 shared_ptr 指向持有的对象,则析构对象
◦operator=对 shared_ptr 赋值
◦reset替换所管理的对象
◦swap 交换所管理的对象
◦get返回存储的指针
◦operator*operator->解引用存储的指针
◦use_count 返回 shared_ptr 所指对象的引用计数
◦operator bool 检查是否有关联的管理对象
◦owner_before提供基于拥有者的共享指针排序

std::weak_ptr

是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(「弱」)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。

std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

◦(构造函数)构造新的weak_ptr
◦(析构函数) 销毁 weak_ptr
◦operator=为weak_ptr赋值
◦reset释放被管理对象的所有权
◦swap交换被管理对象
◦use_count 返回管理该对象的 shared_ptr 对象数量
◦expired检查被引用的对象是否已删除
◦lock 创建管理被引用的对象的shared_ptr
◦owner_before提供弱指针的基于拥有者顺序

std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。

在下列两者之一发生时用关联的删除器释放对象:
◦销毁了管理的 unique_ptr 对象
◦通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。

◦通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。

◦unique_ptr 亦可以不占有对象,该情况下称它为空 (empty)。

◦std::unique_ptr 有两个版本:

  1. 管理单个对象(例如以 new 分配)

  2. 管理动态分配的对象数组(例如以 new[] 分配)

shared_ptr

是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:
◦最后剩下的占有对象的 shared_ptr 被销毁;
◦最后剩下的占有对象的 shared_ptr 被通过 operator= 或 reset() 赋值为另一指针。

用 delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象。

shared_ptr 能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在占有其所属对象时,指向成员对象。存储的指针为 get() 、解引用及比较运算符所访问。被管理指针是在 use_count 抵达零时传递给删除器者。

shared_ptr 亦可不占有对象,该情况下称它为空 (empty) (空 shared_ptr 可拥有非空存储指针,若以别名使用构造函数创建它)。

shared_ptr 的所有特化满足可复制构造 (CopyConstructible) 、可复制赋值 (CopyAssignable) 和可小于比较 (LessThanComparable) 的要求并可按语境转换为 bool 。

成员函数

(构造函数)构造新的 shared_ptr
(析构函数) 如果没有更多 shared_ptr 指向持有的对象,则析构对象
operator= 对 shared_ptr 赋值
reset 替换所管理的对象
swap 交换所管理的对象
get 返回存储的指针
operator*operator-> 解引用存储的指针
use_count 返回 shared_ptr 所指对象的引用计数
operator bool 检查是否有关联的管理对象
owner_before提供基于拥有者的共享指针排序
make_sharedmake_shared_for_overwrite
allocate_sharedallocate_shared_for_overwrite
get_deleter返回指定类型中的删除器,若其拥有
operator<< 将存储的指针的值输出到输出流
std::swap(std::shared_ptr)特化 std::swap 算法

简单的实现一下shared_ptr 和weak_ptr

#include<iostream>
using namespace std;
class Counter{
public:
	Counter(int scnt=1,int wcnt=0):scnt(scnt),wcnt(wcnt){
		cout<<"Counter "<<endl;
	}
	int scnt,wcnt;
	~Counter(){
		cout<<"~Counter"<<endl;
	}
};
template<class T>
class Wptr;

template<class T>
class Shpr{
public:

	Shpr(T * ptr=nullptr):ptr(ptr){
		if(ptr!=nullptr){
			pct=new Counter(1,0);
		}else{
			pct=nullptr;
		}
	}	
	Shpr(const Shpr& sp):ptr(sp.ptr),pct(sp.pct){
		if(pct!=nullptr){
			pct->scnt++;

		}
	}
	void reset(){
		if(pct!=nullptr&&ptr!=nullptr){
			--(pct->scnt);
			if(pct->scnt==0){
				delete ptr;
				if(pct->wcnt==0){
					delete pct;
				}

			}
		}
		pct=nullptr;
		ptr=nullptr;
	}

	void reset(T *ptr){
		reset();
		this->ptr=ptr;
		if(ptr!=nullptr){
			pct=new Counter(1,0);
		}
	}
	~Shpr(){
		reset();
	}
	int use_count(){
		return pct==nullptr?0:pct->scnt;
	}
	Shpr(Shpr &&sp){
		ptr=sp.ptr;
		pct=sp.pct;
		sp.ptr=nullptr;
		sp.pct=nullptr;
	}
	Shpr & operator=(Shpr &&sp){
		if(ptr!=sp.ptr){

		}

		return *this;
	}

	T & operator *(){
		return *ptr;
	}
	T * operator ->(){
		return ptr;
	}
	operator bool(){
		return ptr==nullptr;
	}
	friend class Wptr<T>;
private:
	T* ptr;
	Counter * pct;

		
};


template<class T>
class Wptr{
public:
	Wptr(){
		ptr=nullptr;
		pct=nullptr;

	}
	Wptr(Shpr<T> &sp){
		ptr=sp.ptr;
		pct=sp.pct;
		if(pct!=nullptr	){
			pct->wcnt++;

		}

	}

	void reset(){
		if(pct!=nullptr){
			--pct->wcnt;
			if(pct->scnt==0&&pct->wcnt==0){
				delete pct;
			}

		}
		pct=nullptr;
		ptr=nullptr;
	}
	~Wptr(){
		reset();	
	}	

	Wptr(const Wptr<T>& wp){
		ptr=wp.ptr;
		pct=wp.pct;
		if(pct!=nullptr){
			pct->wcnt++;
		}
	}
	Wptr(Wptr<T> && wp){
		ptr=wp.ptr;
		pct=wp.pct;
		wp.ptr=nullptr;
		wp.pct=nullptr;
	}
	Wptr& operator =(const Wptr<T> & wp){
		if(ptr!=wp.ptr){
			reset();
			ptr=wp.ptr;
			pct=wp.pct;
			if(pct!=nullptr){
				pct->wcnt++;
			}
		}
		return *this;
	}
	Wptr& operator =(const Wptr<T> && wp){
		if(ptr!=wp.ptr){
			reset();
			ptr=wp.ptr;
			pct=wp.pct;
			wp.ptr=nullptr;
			wp.pct=nullptr;

		}
	}
	Wptr& operator=(const Shpr<T>& sp){
		reset();
		ptr=sp.ptr;
		pct=sp.pct;
		if(sp.pct!=nullptr){
			sp.pct->wcnt++;
		}
	}
	int use_count(){
		return pct==nullptr?0:pct->scnt;
	}
	bool expired(){
		return pct==nullptr||pct->scnt==0;
	}
	Shpr<T> lock(){
		Shpr<T> sp(ptr);
		sp.ptr=ptr;
		sp.pct=pct;
		if(pct!=nullptr){
			pct->scnt++;
		}
		return sp;
	}
private:
	T* ptr;
	Counter * pct;

};
int main(){
	
	Shpr<int> sp(new int(1002));
	cout<<sp.use_count()<<endl;
	{
	Wptr<int> wp1(sp);
	cout<<wp1.use_count()<<endl;
	Shpr<int> sp1(sp);
	cout<<sp1.use_count()<<endl;
	}
	cout<<sp.use_count()<<endl;

}


实现时需要注意一点,shared_ptr 和weak_ptr同时实现时,这两个智能指针类都需要有一个计数器的指针,这个计数器类里面(如果智能指针指向一个对象的话)要存指向这个对象的shared_ptr 和weak_ptr 的数量,之后要依据这两个数量来确定是否释放这个计数器,应该是当shared_ptr和weak_ptr的技术都为0时才释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值