当前位置:   article > 正文

C++新特性(五)智能指针_c++智能指针 数组

c++智能指针 数组


不同于java的垃圾回收机制,C++需要程序员自己申请资源,使用完资源以后还要记得释放资源,例如new和delete要配套使用;文件打开以后使用完要关闭;数据库连接以后使用完要断开…这都是程序员需要额外注意的点。忘记了资源释放,会造成资源泄露。为了做到智能释放使用完的资源,C++有了智能指针的技术。结合类使用了引用计数的想法,使程序员不用再担心资源释放的问题。(RAII思想)
下面先介绍一下RAII思想

1,RAII思想

众所周知,一个类对象创建会自动调用构造函数,析构会自动调用析构函数。因此我们可以利用此机制自动解决资源的申请和释放问题,例如:
在这里插入图片描述
RAII,完整的英文是Resource Acquisition Is Initialization,是C++特有的资源管理方式。在主流的语言中,C++是唯一一个依赖RAII来做资源管理的。RAII依托栈和析构函数,来对所有的资源(包括堆内存在内)进行管理,对RAII的使用,使得C++不需要类似于java那样的垃圾收集方法,也能有效地对内存进行管理。具体而言,C++11的stl中带来了3中智能指针,正确合理的使用可以帮助我们管理资源,当然,在C++中使用智能指针不像在java,python中使用垃圾回收机制那么舒适,毕竟程序员还是需要做一些额外的事情,但这远比原来的C++更加方便。这三种智能指针分别是

  • unique_ptr
  • shared_ptr(强指针)
  • weak_ptr(弱指针)
    而在早期有一个auto_ptr,这四种指针在使用上有区别:auto_ptr是有缺陷过时的产物;unique_ptr对auto_ptr的问题做了修正;shared_ptr使用了引用计数,但是会出现循环引用的问题,需要配合后面的weak_ptr一起使用。
    ——————————————————————
    接下来先来介绍智能指针背后的原理,最后再介绍一下这四个智能指针,相信看到最后的读者一定会有醍醐灌顶的感觉。

2,模拟实现智能指针

2.1 实现指针的自动释放

首先,如果将指针要申请的资源放在一个类当中的话,在析构的时候可以自动释放资源,我们的简单目的已经达成。相当于这个类管理了一个指针,可以让他自动释放,相当于模拟了char* p=new char[255];同时也可以自动释放。

#define _crt_secure_no_warnings
#include<iostream>
#include <cstring>
using namespace std;
class SmartPtr {
   
private:
	char* m_Pname;
public:
	SmartPtr(const char*a){
   
		m_Pname = new char[255];
		strcpy(m_Pname, a);
	}
	~SmartPtr()
	{
   
		if (m_Pname != nullptr)
		{
   
			delete m_Pname;
		}
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.2 实现指针用另一指针赋值

上文仅仅模拟了一个指针的动态申请内存,并且可以做到对该指针做自动释放而已,如果要模拟char* m_Pname2=m_Pname呢?,这条语句m_pname2指向m_Pname指向的一块内存。用类来模拟的话其实就是浅拷贝。可以试验调试下面的程序,不过因为他是浅拷贝,所以析构时会有内存泄漏。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <cstring>
using namespace std;
class SmartPtr {
   
private:
	char* m_Pname;
public:
	SmartPtr(const char* a) {
   //在构造函数中动态申请m_Pname资源
		m_Pname = new char[255];
		strcpy(m_Pname, a);
	}
	SmartPtr(SmartPtr& obj) {
   //拷贝构造函数
		m_Pname = obj.m_Pname;
	}
	SmartPtr& operator=(SmartPtr& obj) {
   
		m_Pname = obj.m_Pname;
		return *this;
	}
	~SmartPtr()//析构函数
	{
   
		if (m_Pname != nullptr)
		{
   
			delete m_Pname;
		}
	}
};

int main()
{
   
	{
   
		SmartPtr obj1("张三");//读者可将断点打在此处进行调试进行观察
		SmartPtr obj2 = obj1;
	}//约束在一个块作用域中查看析构
	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
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

现在obj2对象管理的obj2.m_Pname和obj1.m_Pname就指向了同一块堆内存

2.3 解决在实现指针用另一指针赋值过程中的重复释放过程

但是我们又知道浅拷贝在析构时有重复释放的bug,于是就有了引用计数的想法——每拷贝一个对象,引用计数器加一;每个对象析构的时候要查看引用计数器的值,只有引用计数器的值为1时才释放资源。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <cstring>
using namespace std;
class SmartPtr {
   
private:
	char* m_Pname;
	int* m_pCount;//引用计数器,注意引用计数器设置为int*变量,这样的话每个对象的都操作的自己的指针变量指向
	//的都是同一片内存*m_pCount。不能直接用int类型,而且也不能用static,因为这样的话就相当于只能有一个计数器
	//而指针的话我们以后写时拷贝的话可以再申请计数器,这一点介绍到写时拷贝再看就理解了
public:
	SmartPtr(const char* a) {
   //在构造函数中动态申请m_Pname资源
		m_Pname = new char[255];
		m_pCount = new int;
		strcpy(m_Pname, a);
		*m_pCount = 1;
	}
	SmartPtr(SmartPtr& obj) {
   //拷贝构造函数
		m_Pname = obj.m_Pname;
		m_pCount = obj.m_pCount;
		(*m_pCount)++;
	}
	SmartPtr& operator=(SmartPtr& obj) {
   
		m_Pname = obj.m_Pname;
		m_pCount = obj.m_pCount;
		(*m_pCount)++;
		return *this;
	}
	void release() {
   
		(*m_pCount)--;
		if (*m_pCount == 0)
		{
   
			delete m_Pname;
		}
	}
	~SmartPtr()//析构函数
	{
   
		release();
	}
};

int main()
{
   
	{
   
		SmartPtr obj1("张三");//读者可将断点打在此处进行调试进行观察
		SmartPtr obj2 = obj1;
	}//约束在一个块作用域中查看析构
	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
  • 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

可以再将这个引用计数封装成一个类

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <cstring>
using namespace std;

struct 
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/781631
推荐阅读
相关标签
  

闽ICP备14008679号