• c++ || 智能指针


    i am stu
    //cin scanf输入数据会将空格后面刷掉
    cin.getline() //获取到一行

    在这里插入图片描述

    RAII(Resource Acquisition is Inintialization)

    获取资源即初始化,使用局部对象来管理资源的技术称为资源获取即初始化

    资源:操作系统中有限的东西如内存,网络套接字,互斥量,文件句柄
    局部对象:存储在栈的对象,生命周期有操作系统管理

    • 资源的使用
      1获取资源(构造对象)
      2使用资源
      3销毁对象(析构对象)
    • RAII过程四个步骤
      1设计一个类封装资源
      2构造函数中初始化
      3析构函数中执行销毁操作
      4使用时定义一个该类的对象

    如何让资源自动销毁:使用RAII,利用c++语言局部对象自动销毁的特性来控制资源的生命周期

    堆上空间进行自动化管理 – 利用对象自动析构的机制

    int* p = new int(10); //裸指针
    p生成临时auto_ptr对象 — 隐式构造
    使用临时对象拷贝构造a_p
    析构临时对象

    • 裸指针存在的问题

    1.难以区分指向的是单个对象还是一个数组;
    2使用完指针之后无法判断是否应该销毁指针,因为无法判断指针是否“拥有”指向的对象;
    3.在已经确定需要销毁指针的情况下,也无法确定是用delete关键字删除,还是有其他特殊的销毁机制,例如通过将指针传入某个特定的销毁函数来销毁指针;
    4.即便已经确定了销毁指针的方法,由于1的原因,仍然
    无法确定到底是用delete(销毁单个对象)还是delete[(销毁一个数组);

    5.假设上述的问题都解决了,也很难保证在代码的所有路径中(分支结构,异常导致的跳转),**有且仅有一次销毁指针操作;**任何一条路径遗漏都可能导致内存泄露,而销毁多次则会导致未定义行为;
    6.理论上没有方法来分辨一个指针是否处于悬挂状态;

    auto_ptr (c11弃用)

    1. 动态分配对象new,malloc以及当对象不再需要是自动执行清理
    2. 拷贝构造和等号运算符重载将源智能指针置空
    3. 不允许隐式构造

    构造函数与析构函数

    • auto_ptr 析构时会删除他所拥有的对象,即两个auto_ptr不能同时拥有同一个对象,不能使用同一个裸指针赋值/初始化多个auto_ptr
    int *p = new int(0);
    auto_ptr<int> ap(p);
    auto_ptr<int> bp(p);//X 不能指向同一个对象
    
    • 1
    • 2
    • 3
    • auto_ptr析构函数使用的是delete ,而不是delete[] ,不应该用auto_ptr来管理数组指针
    1. ~Mauto_ptr()
      {
      delete _ptr; //对于数组也是直接delete
      }
      // 在析构时 他针对所有的参数类型都是直接进行delete,像数组需要delete[]_ptr;他也是直接delete掉 是他被淘汰的原因之一
    2. 它的release()和reset()都将当前指针置为空,无意义
    template<class _Ty>
    class MyDeletor  //完全泛化的删除器
    {
    public:
    	//MyDeletor() = default;
    	MyDeletor() {}
    	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;
    		}
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    拷贝构造与赋值函数

    • auto_ptr的拷贝构造函数和赋值函数的参数为引用而不是常引用
    • 拷贝或赋值的目标对象将先释放自己原来所拥有的对象
    1. auto_ptr被拷贝或被赋值后,已失去对原对象的所有权,这时对这个auto_ptr的提领(dereference)操作是不安全的

    注意:一定要避免auto_ptr作为函数参数按值传递, 在函数调用时 函数的作用域会产生一个局部对象来接收传入的auto_ptr(拷贝构造),传入的实参就是去对原有对象的所有权,而该对象会在函数退出时被局部auto_ptr删除

    int *p = new int(0);
    auto_ptr<int> ap1(p);
    auto_ptr<int> ap2 = ap1;
    cout<<*ap1;//X ap1已经失去对q指针的所有权
    
    • 1
    • 2
    • 3
    • 4
    1. 因为auto_ptr不具有值语义,所以auto_ptr不能被用在STL标准容器中。

    值语义(value semantics):所有内置类型都具有值语义
    什么样的类没有值语义?(none-value-semantics type,NVST)
    1.有virtual function的类
    2.包含NVST成员的类
    3.NVST的衍生类
    4.定义自己的=operator的类
    5.继承virtual基类的衍生类

    1. 提领操作(dereference)
      a.返回其所拥有的对象的引用
      b.实现了通过auto_ptr调用其所拥有的对象的成员

    注意事项

    1. auto_ptr不能指向数组
    2. auto_ptr不能共享所有权
    3. auto_ptr不能通过复制操作来初始化
    4. auto_ptr不能放入容器中使用
    5. auto_ptr不能作为容器的成员

    unique_ptr(唯一性智能指针)

    c11中使用unique_ptr替代auto_ptr

    1. unique_ptr对象不能进行复制操作,只能进行移动操作
    2. unique_ptr可以‘独占’的拥有他所指向的对象
    3. unique_ptr在某一时刻只能有一个指针指向该对象,两个unique_ptr不能指向同一个对象
    4. unique_ptr对象中保存指向某个对象的指针,当他本身本身被删除或离开作用域时会自动释放其所指向对象所占用的资源

    创建

    将一个new操作符返回的指针传递给unique_ptr的构造函数

    unique_ptr<int> pInt(new int(5));
    cout<<*pInt;
    
    • 1
    • 2

    无法进行复制构造和赋值操作

    可以进行移动构造和移动赋值操作

    可以用过std::move()函数转移指针的所有权

    unique_ptr<int> pInt(new int(5));
    unique_ptr<int> pInt2 = std::move(pInt);//转移所有权
    cout<<*pInt;//错误 
    cout<<*pInt2;//正确
    unique_ptr<int> pInt3(std::move(pInt2));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用场景

    1. 为动态申请资源提供安全保证
      只要unique_ptr指针创建成功,其析构函数都会被调用,确保动态资源被释放
    2. 返回函数内动态申请资源的所有权
    unique_ptr<int> func( int x)
    {
    	unique_ptr<int> pInt(new int(x)) ;
    	return pInt; //返回unique_ptr5.
    }
    int main( )
    {
        int a = 5;
        unique_ptr<int> ret = func(a);
        cout <<*ret << endl;
    }
    //函数结束后,自动释放资源
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 容器内保存指针
    int main( ) {
    vector<unique_ptr<int> > vec;
    unique_ptr<int> pInt(new int(5));
    vec.push_back(std : :move(pInt)); //使用移动语义
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 管理动态数组
    int main() 
    {
    	unique_ptr<int[ ]> p(new int[5] {12345});
    	p[0] = 0; //重载了operator[]
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // class unique_ptr 
    template<class _Ty,class _Dx = MyDeletor<_Ty> >
    class my_unique_ptr
    {
    public:
    	// old 
    	//typedef _Ty* pointer;
    	//typedef _Ty  element_type;
    	//typedef _Dx  delete_type;  删除器
    	// c11 new 
    	using pointer = _Ty*;      //typedef _Ty* pointer;
    	using element_type = _Ty;  //typedef _Ty  element_type;
    	using delete_type = _Dx;   //typedef _Dx  delete_type;
    private:
    	_Ty* _Ptr;
    	_Dx  _myDeletor;//空的 占位符 占1字节 对其方式就站4字节
    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) { cout << "my_unique_ptr: " << this << endl; }
    	~my_unique_ptr()
    	{
    		if (_Ptr != nullptr)
    		{
    			_myDeletor(_Ptr);
    			_Ptr = nullptr;
    		}
    		cout << "~my_unique_ptr: " << this << endl;
    	}
    	my_unique_ptr(my_unique_ptr&& _Y)
    	{ //移动构造
    		_Ptr = _Y._Ptr;
    		_Y._Ptr = nullptr;
    		cout << " move copy my_unique_ptr : " << this << endl;
    	}
    	template<class _Uy>
    	my_unique_ptr& operator=(_Uy* _p )
    	{ //移动赋值
    		if (this->_Ptr ==(_Ty*) _p) return *this;
    		if (_Ptr != nullptr) { _myDeletor(_Ptr); }
    		_Ptr = _p;
    		return *this;
    	}
    	my_unique_ptr& operator=(my_unique_ptr&& _Y)
    	{
    		if (this == &_Y) return *this;
    		reset(_Y.release());
    		//if (_Ptr != nullptr) { _myDeletor(_Ptr); }
    		//_Ptr = _Y._Ptr;
    		//_Y._Ptr = nullptr;
    		cout << " move opertor=: " << endl;
    		return *this;
    	}
    	_Dx & get_deleter()  
    	{
    		return _myDeletor;
    	}
    	const _Dx& get_deleter() const
    	{
    		return _myDeletor;
    	}
    	_Ty& operator*() const //重载*
    	{
    		return *_Ptr;
    	}
    
    	pointer operator->() const
    	{
    		return &**this;// _Ptr; 
    	}
    	pointer get() const
    	{
    		return _Ptr;
    	}
    	operator bool() const //强转
    	{
    		return _Ptr != nullptr;
    	}
    	pointer release() //释放
    	{
    		_Ty* 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);
    	}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    shared_ptr(共享指针)

    • 引用计数指针,用于共享对象的所有权,允许多个指针指向同一个对象
    • shared_ptr使用‘引用计数’方法来管理对象资源,每个shared_ptr对象关联一个共享的引用计数
    • shared_ptr在拷贝或赋值时,引用计数加1,当被赋予一个新值或被销毁时,引用计数减1,当计数器变为0,他就会自动释放自己所管理的对象
    • 计数器的值不存储在shared_ptr内,存储在堆上。
    • p=q;p的引用计数值会减少,q的引用计数值会增加
    class Object {
    private:
    	int value;
    public:
    	Object(int x = 0)
    		:value(x)
    	{
    		cout << "create Object" << endl;
    	}
    	~Object() { cout << "destory Object" << endl; }
    	int& Value() { return value; }
    	const int& Value()const { return value; }
    
    };
    
    int main()
    {
    	shared_ptr<Object> obj(new Object(100));
    	cout << (*obj).Value() << endl;  
    	cout << "obj引用计数" << obj.use_count() << endl;
    	shared_ptr<Object> obj2 = obj;
    	cout << "obj引用计数" << obj.use_count() << endl;
    	cout << "obj2引用计数" << obj2.use_count() << endl;
    	 //1 2 2 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    创建

    两种方法

    1. 通过自己的构造函数
      shared_ptr由一块内存的原生指针创建的时候(原生内存:Object还没有其他shared_ptr指向这块内存),计数器随着产生,会一直存在,直至所有的shared_ptr和weak_ptr都被销毁的时候。
      当所有的shared_ptr都被销毁时,这块内存就已经被释放了,但可能weak_ptr还存在导致计数器的释放可能发生在内存对象销毁后很久才发生。
      o
      在堆上实际发生了两次内存分配
    Object ptr = new Object(10);
    std:shared_ptr<Object> pObj(ptr);//分配内存给shared_ptr 的计数器
    std:.shared_ptr< Object> pObj(new Object(10));
    
    
    • 1
    • 2
    • 3
    • 4
    1. 通过std::make_shared()
      可以同时为计数器和原生内存分配内存空间,并把二者的内存是坐整体管理

    在这里插入图片描述

    优点缺点
    减少单词内存分配的次数,可以增大cache局部性有可能被管理的对象已经被析构了,但是他的空间还在,内存没有归还,他仍在等在weak_ptr都被清除之后才和计数器的空间一起被归还

    在这里插入图片描述

    检查引用计数

    两个函数用来检查共享的引用计数值
    1.unique():测试该shared_ptr是否是原始指针唯一拥有者,use_count() = 1时返回TRUE;否则false

    2.use_count()返回当前指针的引用计数值,效率低,只用来测试或调试

    线程安全性

    1.引用计数本身是线程安全(引用计数是原子操作)
    2.多个线程同时读同一个shared_ptr对象时线程安全的
    3.多个线程同时对同一个shared_ptr对象进行读和写,则需要加锁

    数据结构:堆上存放计数值

    在这里插入图片描述

    weak_ptr(弱共享)

    • 为配合shared_ptr而引入非智能指针,指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数
    • weak_ptr并不拥有对动态对象的管辖权

    创建

    需要用一个shared_ptr实例来初始化weak_ptr

    int main()
    {
    	shared_ptr<int> sp(new int(5));
    	cout << "创建前sp的引用计数:" << sp.use_count() << endl; // use_count = 1
    	weak_ptr<int> wp(sp);
    	cout << "创建后sp的引用计数:" << sp.use_count() << endl; // use_count = 1
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如何判断weak_ptr指向对象是否存在

    • 由于weak ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,我们就不能使用weak_ptr直接访问对象。
    • C++中提供了lock 函数来实现该功能。
      如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr
    class Object
    {
    private:
    	int value;
    public:
    	Object(int x = 0) :value(x) { cout << "creat Object" << endl; }
    	~Object() { cout << "deatory Object" << endl; }
    	int GetValue() {
    		return value;
    	}
    };
    int main()
    {
    	shared_ptr<Object> sp(new Object(5));//动态开辟 用5初始化
    	weak_ptr<Object> wp(sp);   //用shared_ptr实例初始化weak_ptr
    	if (shared_ptr<Object> pa = wp.lock())
    	{
    		cout << pa->GetValue() << endl;
    	}
    	else
    	{
    		cout << wp.expired() << endl;
    		cout << "wp所指对象为空" << endl;
    	}
    		return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    使用weak_ptr

    不能直接通过weak_ptr直接访问对象,可以通过lock()获得shared_ptr,进而访问原始对象

    在这里插入图片描述
    在这里插入图片描述

    Boost智能指针

    shared_ptr< T >有一个引用计数器,表示T的对象是否不再使用
    scoped_ptr< T >当离开作用域能够自动释放的指针,不传递所有权
    intrusive_ptr< T >提供自己的指针使用引用计数机制
    shared_array< T >用来处理数组
    scoped_array< T >用类处理数组的
  • 相关阅读:
    Excel表格数据导入MySQL数据库
    使用 Visual Studio Code 编写 TypeScript程序
    zabbix5.0 通过SNMPv3 监控华三交换机
    MySQL——数据库设计三范式
    Nginx系列教程(一)| 手把手教你在Linux环境下搭建Nginx服务
    【CKS】考试之TLS通信配置
    2.3 IOC之于注解管理bean
    sql 2
    计算机毕设 机器学习股票大数据量化分析与预测系统 - python 计算机毕设
    Vue 条件渲染 与 列表渲染
  • 原文地址:https://blog.csdn.net/LQEmptycity/article/details/121640605