IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++ 智能指针 -> 正文阅读

[C++知识库]C++ 智能指针

什么是RAII

RAII(Resource Acquisition Is Initialization)是由C++之父提出的,中文翻译为资源获取即初始化,使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存(heap)、网络套接字、互斥量、文件句柄等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入

RAII的原理

资源的使用一般经历三个步骤:

  1. 获取资源(创建对象)
  2. 使用资源
  3. 销毁资源(析构对象)

但是资源的销毁往往是程序员经常忘记的一个环节,所以程序界就想如何在程序中让资源自动销毁呢?解决问题的方案就是:RAII,它充分的利用了C++语言局部对象自动销毁的特性来控制资源的生命周期

裸指针存在的问题

  1. 难以区分指向的是单个对象还是一个数组
  2. 使用完指针之后无法判断是否应该销毁指针,因为无法判断指针是否”拥有“指向的对象
  3. 在已经确定需要销毁指针的情况下,也无法确定是用delete关键字删除,还是有其他特殊的销毁机制,例如通过将指针传入某个特定的销毁函数来摧毁指针
  4. 即使已经确定了销毁指针的方法,由于1的原因,仍然无法确定到底是i用delete(销毁单个对象)还是delete[](销毁一个数组)
  5. 假设上述的问题都解决了,也很难保证在代码的所有路径中(分支结构,异常导致的挑战),有且仅有一次销毁指针的操作;任何一条路径遗漏都可能导致内存的泄露,而销毁多次则会导致未定义行为
  6. 理论上没有方法来分辨一个指针是否处于悬挂状态

auto_ptr

class Object
{
	int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destory Object:" << this << endl;
	}
	int& Value()
	{
		return value;
	}
};

template<class _Ty>
class my_auto_ptr
{
private:
	bool _Owns;
	_Ty* _Ptr;
public:
	my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p)
	{}
	~my_auto_ptr()
	{
		if (_Owns)
		{
			delete _Ptr;
		}
		_Owns = false;
		_Ptr = NULL;
	}
};

void fun()
{
	my_auto_ptr<Object> obj(new Object(10));
}

int main()
{
	fun();
}

在这里插入图片描述
在这里插入图片描述
在这里将Object构建完成后,将其指针给到p,当函数结束去调动智能指针的析构函数去释放空间

若我们需要在fun()函数中,去调用Object类的方法obj->Value();

class Object
{
	int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destory Object:" << this << endl;
	}
	int& Value()
	{
		return value;
	}
};

template<class _Ty>
class my_auto_ptr
{
private:
	bool _Owns;
	_Ty* _Ptr;
public:
	my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p)
	{}
	~my_auto_ptr()
	{
		if (_Owns)
		{
			delete _Ptr;
		}
		_Owns = false;
		_Ptr = NULL;
	}
	_Ty* get()const
	{
		return _Ptr;
	}
	_Ty& operator*()const
	{
		return *(get());
	}
	_Ty* operator ->()const
	{
		return get();
	}
};

void fun()
{
	my_auto_ptr<Object> obj(new Object(10));
	cout << obj->Value() << endl;
	cout << (*obj).Value() << endl;
}

int main()
{
	fun();
}

在这里插入图片描述
通过运算符重载,(*obj) 后将直接指向堆区(heap)的对象实体

若我们通过一个my_auto_ptr去创建另一个my_auto_ptr

class Object
{
	int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destory Object:" << this << endl;
	}
	int& Value()
	{
		return value;
	}
};

template<class _Ty>
class my_auto_ptr
{
private:
	bool _Owns;
	_Ty* _Ptr;
public:
	my_auto_ptr(_Ty* p = NULL) :_Owns(p != NULL), _Ptr(p)
	{}
	~my_auto_ptr()
	{
		if (_Owns)
		{
			delete _Ptr;
		}
		_Owns = false;
		_Ptr = NULL;
	}
	my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(obj._ptr)
	{	
	}
	my_auto_ptr& operator=(const my_auto_ptr& _Y)
	{
		if(this == &_Y) return *this;
		if(_Owns)
		{
			delete _Ptr;
		}
		_Owns = _Y._Owns;
		_Ptr = _Y._Ptr;
		return 0;
	}

	_Ty* get()const
	{
		return _Ptr;
	}
	_Ty& operator*()const
	{
		return *(get());
	}
	_Ty* operator ->()const
	{
		return get();
	}
	void reset(_Ty* p = NULL)
	{
		if (_Owns)
		{
			delete _Ptr;
		}
		_Ptr = p;
	}
	_Ty* release()const
	{
		_Ty* tmp = NULL;
		if (_Owns)
		{
			((my_auto_ptr*)this)->_Owns = false; //常性进行修改
			tmp = _Ptr;
			((my_auto_ptr*)this)->_Ptr = NULL;
		}
		return tmp;
	}
};

void fun()
{
	my_auto_ptr<Object> pobja(new Object(10));
	my_auto_ptr<Object> pobjb(pobja);
}

int main()
{
	fun();
}

如果通过浅拷贝,则两个指针拥有同一个资源,在析构的过程会造成资源的重复释放导致崩溃

若设置为将其资源进行转移

my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(release())
{
}
my_auto_ptr& operator=(const my_auto_ptr& _Y)
{
	if(this == &_Y) return *this;
	if(_Owns)
	{
		delete _Ptr;
	}
	_Owns = _Y._Owns;
	_Ptr = _Y.release();
	return 0;
}
void fun(my_auto_ptr<Object> apx)
{
	int x = apx->Value();
	cout<<x<<endl;
}
int main()
{
	my_auto_ptr<Object> pobja(new Object(10));
	fun(pobja);
	int a = pobja->Value();
	cout<<a<<endl;
}

那么上面的过程中,资源会进行转移pobja将不再拥有资源,导致pobja失去资源进而程序崩溃

这也就是auto_ptr的局限性,也导致该智能指针的几乎没有使用

unique_ptr

该智能指针属于唯一性智能指针,将拷贝构造删除,也就不能将其新建另一个对象,同时也不能作为参数传入

class Object
{
	int value;
public:
	Object(int x = 0) :value(x)
	{
		cout << "Create Object:" << this << endl;
	}
	~Object()
	{
		cout << "Destory Object:" << this << endl;
	}
	int& Value()
	{
		return value;
	}
};

int main()
{
	std::unique_ptr<Object> pobja(new Object(10));
	//std::unique_ptr<Object> pobjb(pobja); error
	//不允许
	std::unique_ptr<Object> pobjb(std::move(pobja));
}

通过移动赋值是可以的,通过明确的概念,对其资源进行转移

同时unique_ptr可以区分其所指向的是一个单独空间,或者是连续的空间

struct delete_ar_object
{
	void operator()(Object* op)
	{
		if(op == NULL) return;
		delete[] op;
	}
}
int main()
{
	std::unique_ptr<Object> pobja(new Object(10));
	std::unique_ptr<Object,delete_ar_object> pobjb(new Object[10]);
}

在这里如果是连续空间,会调用删除连续空间的删除器;单独空间则使用默认删除器

unique_ptr在编写的时候,有多个模板类,分别对应单个对象的方案和一组对象的方案

并且可以通过智能指针指向fopen打开的文件对象,而文件对象是同fclose去进行关闭的

struct delete_file
{
	void operator()(FILE *fp)
	{
		if(fp == NULL) return;
		fclose(fp);
	}
}
std::unique_ptr<FILE,delete_file> pfile(fopen("zyq.txt","w"));

这里只需要将默认的删除器,更改为对文件对象的删除器

my_unique_ptr 代码

template<class _Ty>
class MyDeletor //可接受一切类型方案
{
public:
	MyDeletor() = default;//默认构造函数
	void operator()(_Ty* ptr) const
	{
		if (ptr != nullptr)
		{
			delete ptr;
		}
	}
};
template<class _Ty>
class MyDeletor<_Ty[]> //部分特化
{
public:
	MyDeletor() = default;
	void operator()(_Ty* ptr)const
	{
		if (ptr != nullptr)
		{
			delete[] ptr;
		}
	}
};

template<class _Ty,class _Dx = MyDeletor<_Ty>> //默认删除器
class my_unique_ptr
{
public:
	// old 
	//typename _Ty* pointer;
	//typedef _Ty element_type;
	//typedef _Dx delete_type;
	// c11
	using pointer = _Ty*;
	using element_type = _Ty;
	using delete_type = _Dx;

private:
	_Ty* _Ptr;
	_Dx _myDeletor;
public:
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;

	my_unique_ptr(pointer _P = nullptr) :_Ptr(_P) {}
	~my_unique_ptr()
	{
		if (_Ptr != nullptr)
		{
			_myDeletor(_Ptr);
			_Ptr = nullptr;
		}
	}
	my_unique_ptr(my_unique_ptr&& _Y)
	{
		_Ptr = _Y._Ptr;
		_Y._Ptr = nullptr;
	}
	my_unique_ptr& operator=(my_unique_ptr&& _Y)
	{
		if (this == &_Y) return *this;
		reset(_Y.release()); //资源转移给this
		return *this;

	}
	_Dx& get_deleter()
	{
		return _myDeletor;
	}
	const _Dx& get_deleter()const
	{
		return _myDeletor;
	}
	_Ty& operator*()const
	{
		return *_Ptr;
	}
	pointer operator->()const
	{
		return &**this; //return _Ptr;
	}
	pointer get()const
	{
		return _Ptr;
	}
	operator bool()const
	{
		return _Ptr != nullptr;
	}
	pointer release()
	{
		pointer old = _Ptr;
		_Ptr = nullptr;
		return old;
	}
	void reset(pointer _P = nullptr)
	{
		pointer old = _Ptr;
		_Ptr = _P;
		if (old != nullptr)
		{
			_myDeletor(old);
		}
	}
	void swap(my_unique_ptr _Y)
	{
		std::swap(_Ptr, _Y._Ptr);
		std::swap(_myDeletor, _Y._myDeletor);
	}

};
template<class _Ty, class _Dx> 
class my_unique_ptr<_Ty[],_Dx> // 处理数组的部分特化版本
{
public:
	using pointer = _Ty*;
	using element_type = _Ty;
	using delete_type = _Dx;

private:
	_Ty* _Ptr;
	_Dx _myDeletor;
public:
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;

	my_unique_ptr(pointer _P = nullptr) :_Ptr(_P) {}
	~my_unique_ptr()
	{
		if (_Ptr != nullptr)
		{
			_myDeletor(_Ptr);
			_Ptr = nullptr;
		}
	}
	my_unique_ptr(my_unique_ptr&& _Y)
	{
		_Ptr = _Y._Ptr;
		_Y._Ptr = nullptr;
	}
	my_unique_ptr& operator=(my_unique_ptr&& _Y)
	{
		if (this == &_Y) return *this;
		reset(_Y.release()); //资源转移给this
		return *this;

	}
	_Dx& get_deleter()const
	{
		return _myDeletor;
	}
	const _Dx& get_deleter()const
	{
		return _myDeletor;
	}
	_Ty& operator*()const
	{
		return *_Ptr;
	}
	pointer operator->()const
	{
		return &**this; //return _Ptr;
	}
	pointer get()const
	{
		return _Ptr;
	}
	operator bool()const
	{
		return _Ptr != nullptr;
	}
	pointer release()
	{
		pointer old = _Ptr;
		_Ptr = nullptr;
		return old;
	}
	void reset(pointer _P = nullptr)
	{
		pointer old = _Ptr;
		_Ptr = _P;
		if (old != nullptr)
		{
			_myDeletor(old);
		}
	}
	void swap(my_unique_ptr _Y)
	{
		std::swap(_Ptr, _Y._Ptr);
		std::swap(_myDeletor, _Y._myDeletor);
	}
	_Ty& operator[](size_t _Idx)const
	{
		return _Ptr[_Idx];
	}
};


template<class _Ty,class ..._Type>
my_unique_ptr<_Ty> my_make_unique(_Type&& ... _arys)
{
	return my_unique_ptr<_Ty>(new _Ty(_arys...));

}
using namespace std;

class Object
{
	int value;
	int sum;
	double total;
public:
	Object(int x = 0) :value(x), sum(x), total(0)
	{
		cout << "Create Object" << this << endl;
	}
	Object(int x, float ft) :value(x), sum(x), total(ft)
	{
		cout << "Create Object" << this << endl;
	}
	~Object() 
	{
		cout << "~Object" << endl;
	}
	int& Value() 
	{ return value; }
	const int& Value()const
	{
		return value;
	}
};
int main()
{
	my_unique_ptr<Object> op1 = my_make_unique<Object>(12);
	my_unique_ptr<Object> op2 = my_make_unique<Object>(12, 23.34);

	return 0;
}

在这里使用了可变参数特性,可以根据传入参数数量进行特化

template<class _Ty,class ..._Type>
my_unique_ptr<_Ty> my_make_unique(_Type&& ... _arys)
{
	return my_unique_ptr<_Ty>(new _Ty(_arys...));

}
my_unique_ptr<Object> fun()
{
	my_unique_ptr<Object> opa(new Object(10));
	return opa;
}
int main()
{
	my_unique_ptr<Object> op1 = fun();
	return 0;
}

在这里插入图片描述
在这里首先创建了Object对象与opa对象,通过移动拷贝构造产生了op1对象,并且将opa进行析构,直到程序结束才会析构op1;在这里系统进行了优化,直接将op1构建为移动对象

下面将代码进行修改,导致会调用移动赋值进行资源转移

my_unique_ptr<Object> fun()
{
	my_unique_ptr<Object> opa(new Object(10));
	return opa;
}
int main()
{
	my_unique_ptr<Object> op1;
	op1 = fun();
	return 0;
}

在这里插入图片描述
在这里插入图片描述
无论任何情况,引用返回具名对象,或右值引用返回不具名对象都是错误的

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-31 23:45:54  更:2022-03-31 23:47:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:00:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码