C++ 智能指针_java下一页

(3) 2024-06-02 16:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说C++ 智能指针_java下一页,希望能够帮助你!!!。

智能指针

输入问题:
给定一个字符串,不知道有几个字符,怎样输入?
使用cin.getline(); 输入一行。

智能指针概念

智能指针主要是针对裸指针进行了一次面向对象的封装,在构造函数中初始化资源地址,在析构函数中释放资源。当资源应该被释放时,指向它的智能指针可以确保自动释放它。

RAII

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

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

RALL的原理

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

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

RALL充分利用C++语言局部对象自动销毁的特性来控制资源的生命周期。

malloc, new 等动态分配的对象,很有可能忘记了去释放资源而导致泄露。
对于一个对象而言,在构造函数的时候申请空间,而在析构函数(在离开作用域时调用)的时候释放空间,也就是RAII资源获取即初始化技术。

C++11引入智能指针,使用引用计数,让程序员不再需要关心手动释放内存。

引用计数

这种计数是为了防止内存泄露而产生的。

对于动态分配的对象,进行引用计数,每当增加一次对同一个对象的引用,那么引用计数就会增加一次,每删除一次引用,引用计数就会减一,当一个对象的引用计数为0时,就会自动删除指向的堆内存。

注意:引用计数不是垃圾回收,引用计数能够尽快收回不再使用的对象,同时在回收过程中也不会造成长时间的等待,更能够清晰的表明资源的生命周期。

裸指针

int *ptr;

这种指针被称为裸指针。
使用裸指针会存在一些不足:

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

  2. 如果使用多个裸指针指向同一资源,其中一个指针对资源进行释放,其他指针成为空悬指针,如果再次释放会存在错误。
    C++ 智能指针_java下一页_https://bianchenghao6.com/blog__第1张

  3. 如果程序异常退出时,裸指针的释放资源的代码未能执行,也会造成内存泄漏。

智能指针的分类

需要引入头文件#include< memory >智能指针

不带引用计数的智能指针auto_ptr

已经废弃

  • 不能使用同一个裸指针赋值/初始化多个auto_ptr;
  • 拷贝构造和等号运算符—— 将原智能指针置空;
  • 不允许隐式构造;

auto_ptr是通过将除最后一个以外的其它auto_ptr 置 nullptr 来避免浅拷贝的发生,它的资源所有权是可以转移的。

void fun()
{ 
   
	int* p = new int(10);
	auto_ptr<int> a_p(p); //a_p在栈帧上,它会自动析构,会将堆上的p delete
	//auto_ptr<int> a_p1(p); 崩溃 不能使用同一个裸指针赋值/初始化多个auto_ptr
	//auto_ptr<int> a_p = p; //拷贝构造 隐式构造,根据类型自推
	/* p生成临时auto_ptr对象——隐式构造 使用临时对象拷贝构造a_p 析构临时对象 ——优化-- 直接构造a_p */
	
	//a_p=a_p2; 等号运算符重载
	auto_ptr<int> a_p = auto_ptr<int>(p);//显式构造
	cout << *p << endl;
	cout << *a_p << endl;

	a_p.release();//返回当前指向的地址,并将当前智能指针置空
	a_p.reset();//将当前智能指针指向的内存释放,指针置空
	a_p.get();//获取智能指针内部指针
	
}

//auto_ptr a_p = p; 拷贝构造 不允许隐式构造,根据类型自推

	*p生成临时auto_ptr对象——隐式构造
	* 使用临时对象拷贝构造a_p
	* 析构临时对象
	* ——优化--   直接构造a_p

auto_ptr a_p = auto_ptr §; 显式构造

模拟实现mauto_ptr

#ifndef MAUTO_PTR_H
#define MAUTO_PTR_H

template<typename T>
class Mauto_ptr
{ 
   
public:
	explicit Mauto_ptr(T*ptr=nullptr)//explicit防止隐式构造
		:_ptr(ptr)
	{ 
   }

	Mauto_ptr(Mauto_ptr& src)
		:_ptr(src.release())
	{ 
   }

	Mauto_ptr& operator=(Mauto_ptr& src)//等号运算符重载
	{ 
   
		_ptr = src.release();
	}

	~Mauto_ptr()
	{ 
   
		delete_ptr;
	}

	T* release()
	{ 
   
		T* tmp = _ptr;
		_ptr = NULL;
		return tmp;
	}
	
	void reset()
	{ 
   
		delete_ptr;
		_ptr = NULL;
	}

	T& operator*()//解引用运算符
	{ 
   
		return *_ptr;
	}

	T* operator->()//箭头运算符,是二次调用
	{ 
   
		return _ptr;
	}
private:
	T* _ptr;
};
#endif

不带引用计数的智能指针unique_ptr

C++ 11

  • 不能使用同一个裸指针赋值/初始化多个unique_ptr;
  • 不允许隐式构造;
  • 不允许拷贝构造,不允许等号运算符重载;

unique_ptr 删除了拷贝构造和赋值函数,因此不支持普通的拷贝或赋值操作。
引入了带有右值引用的拷贝构造和等号运算符重载,可以把 unique_ptr 作为函数的返回值。


//右值引用 用来引用即将死亡的对象——临时对象
unique_ptr<int>fun(unique_ptr<int>& ptr)
{ 
   
	cout << *ptr << endl;
	int* p = new int(9);
	return unique_ptr<int>(p);
}


void fun()
{ 
   
	int* p = new int(10);
	unique_ptr<int> u_p;
	//unique_ptr<int> u_p=p; 不允许隐式构造
	//unique_ptr<int> u_p(u_p1);不允许拷贝构造
	//u_p1=u_p ;不允许等号运算符重载
	unique_ptr<int> u_p(p);
	//unique_ptr<int> u_p(new int(10));
	cout << *p << endl;
	cout << *u_p << endl;

	u_p.get();
	u_p.release();
	u_p.reset();
	//u_p.swap(); 交换两个智能指针

	unique_ptr<int> u_p2(fun(u_p));
	u_p2 = fun(u_p);
	cout << *u_p2 << endl;
	
}

右值引用 用来引用即将死亡的对象——临时对象

const int& a = 10;
int&& b = 10;

模拟实现unique_ptr

#ifndef MUNIQUE_PTR_H
#define MUNIQUE_PTR_H


template<typename T>
class Munique_ptr
{ 
   
public:
	explicit Munique_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{ 
   }

	//Munique_ptr(Munique_ptr& src) = delete;

	//Munique_ptr& operator=(Munique_ptr& src) = delete;

	Munique_ptr(Munique_ptr&& src)//右值引用,引用即将死亡的对象
		:_ptr(src.release())
	{ 
   
	}
	Munique_ptr& operator=(Mauto_ptr&& src)
	{ 
   
		_ptr = src.release();
	}

	~Munique_ptr()
	{ 
   
		delete_ptr;
	}

	T* release()
	{ 
   
		T* tmp = _ptr;
		_ptr = NULL;
		return tmp;
	}

	void reset()
	{ 
   
		delete_ptr;
		_ptr = NULL;
	}

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

	T* operator->()//箭头运算符,是二次调用
	{ 
   
		return _ptr;
	}

	operator bool()//bool重载 没有返回值
	{ 
   
		return _ptr != NULL;
	}
private:
	T* _ptr;
};
#endif

带引用计数的智能指针shared_ptr——强智能指针

  • 不允许隐式构造;
  • 不能使用同一个裸指针赋值/初始化多个shared_ptr;
  • 允许拷贝构造,允许等号运算符重载;

一个shared_ptr 对资源进行引用时,资源的引用计数会增加一,通常用于管理对象的生命周期。只要有一个指向对象的shared_ptr 存在,该对象就不会析构。

#include<iostream>
#include<map>
#include<memory>//智能指针
#include"mshared_ptr.h"

using namespace std;

template<typename T>
map<T*, int> Mshared_ptr<T>::_count = new map<T*,int>();


void fun()
{ 
   
	int* p = new int(11);
	shared_ptr<int> s_p(p);
	shared_ptr<int> s_p1(s_p);//允许拷贝构造
	s_p = s_p1;//允许等号运算符重载
	cout << s_p.use_count() << endl;//引用计数器
	cout << *p << endl;
	cout << *s_p << endl;
	cout << *s_p1 << endl;

	s_p.unique();//判断当前是否是唯一的
}

模拟实现shared_ptr

#ifndef MSHARED_PTR_H
#define MSHARED_PTR_H


template<typename T>
class Mshared_ptr
{ 
   
public:
	explicit Mshared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{ 
   }

	
	Mshared_ptr(Mshared_ptr& src)
	{ 
   
		/* //返回值为pair 判断插入是否成功 ==1插入成功,==0插入失败 if (_count.insert(make_pair(_ptr, 2)).second == 0) { _count[_ptr]++; } */
		_count.insert(make_pair(_ptr, 1));
		_count[_ptr]++;

		_ptr = src._ptr;
	}
	Mshared_ptr& operator=(Mshared_ptr&& src)
	{ 
   
		if (_ptr == src._ptr)
		{ 
   
			return *this;
		}

		if (unique())
		{ 
   
			_count.erase(_ptr);
			delete _ptr;
		}
		else
		{ 
   
			_count[_ptr]--;
		}
		_ptr = src._ptr;
	}

	~Mshared_ptr()
	{ 
   
		if (unique())
		{ 
   
			_count.erase(_ptr);
			delete _ptr;
		}
		else
		{ 
   
			_count[_ptr]--;
		}
		_ptr = src._ptr;
	}

	bool unique()
	{ 
   
		if (count.find(_ptr) == count.end()||count[_ptr]==1)//顺序不能变
		{ 
   
			return true;
		}
		return false;
	}
	T* release()
	{ 
   
		T* tmp = _ptr;
		if (unique())
		{ 
   
			_count.erase(_ptr);
		}
		else
		{ 
   
			_count[_ptr]--;
		}
		_ptr = NULL;
		return tmp;
	}
	
	void reset()
	{ 
   
		if (unique())
		{ 
   
			_count.erase(_ptr);
			delete _ptr;
		}
		else
		{ 
   
			_count[_ptr]--;
		}
		_ptr = NULL;
	}

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

	

	T* operator->()//箭头调用,是二次调用
	{ 
   
		return _ptr;
	}

	operator bool()
	{ 
   
		return _ptr!=NULL;
	}
private:
	static map<T*, int>* _count;
	T* _ptr;
};

带引用计数的智能指针weak_ptr——弱智能指针

  • 不能直接使用,不能解引用;
  • 如果要使用必须先转化为强智能指针,用lock()返回强智能指针;
  • 弱智能指针不占用引用计数;
  • 只能由强智能指针构造弱智能指针;

weak_ptr 对资源的引用不会引起资源的引用计数的变化,通常作为观察者,用于判断资源是否存在,并根据不同情况做出相应的操作。

比如使用 weak_ptr 对资源进行弱引用,当调用 weak_ptr的 lock() 方法时,若返回 nullptr ,则说明资源已经不存在,放弃对资源的继续操作。否则,将返回一个 shared_ptr 对象,可以继续操作资源。

一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。即使有 weak_ptr 指向对象,对象也还是会被释放。

C++ 智能指针_java下一页_https://bianchenghao6.com/blog__第2张
C++ 智能指针_java下一页_https://bianchenghao6.com/blog__第3张

class A
{ 
   
public:
	A()
	{ 
   
		cout << "A()" << endl;
	}
	~A()
	{ 
   
		cout << "~A()" << endl;
	}
	weak_ptr<B> _ptr_b;
};

class B
{ 
   
public:
	B()
	{ 
   
		cout << "B()" << endl;
	}
	~B()
	{ 
   
		cout << "~B()" << endl;
	}
	weak_ptr<A> _ptr_a;
};

void fun()
{ 
   

	shared_ptr<A> a_p(new A());
	shared_ptr<B> b_p(new B());

	shared_ptr<int> p(new int(10));//强智能指针构造弱智能指针
	weak_ptr<int> w_p(p);

	cout << *p << endl;
	cout << *(w_p.lock()) << endl;//lock返回强智能指针
	shared_ptr<int> tmp = w_p.lock();

	a_p->_ptr_b = b_p;    //无法析构
	b_p->_ptr_a = a_p;
}

模拟实现mweak_ptr

#ifndef MWEAK_PTR_H
#define MWEAK_PTR_H
#include"mshared_ptr.h"

template<typename T>
class Mweak_ptr
{ 
   
public:
	Mweak_ptr(Mshared_ptr& s_ptr)
	{ 
   
		_s_ptr = s_ptr.get();
		Mshared_ptr<T>::_count.insert(make_pair(_ptr, 1));  //加友元可以访问
	}

	Mshared_ptr<T> lock()
	{ 
   
		if (Mshared_ptr<T>::_count.find(_s_ptr) != Mshared_ptr<T>::_count.end())
		{ 
   
			return Mshared_ptr<T>(_s_ptr);
		}
		return Mshared_ptr<T>();
	}
private:
	T* _s_ptr;
};


#endif

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复