【C++】C++11——智能指针、内存泄漏、智能指针的使用和原理、RAII、auto_ptr、unique_ptr、shared_ptr、weak_ptr

news2024/12/24 18:20:19

文章目录

  • C++11
    • 7.智能指针
      • 7.1内存泄漏
      • 7.2智能指针的概念
      • 7.3智能指针的使用
        • 7.3.1 auto_ptr
        • 7.3.2 unique_ptr
        • 7.3.3 shared_ptr
        • 7.3.4 weak_ptr

C++11

在这里插入图片描述

7.智能指针

7.1内存泄漏

  什么是内存泄漏:

  内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

  内存泄漏通常由于程序在设计上的缺陷或错误,例如动态分配内存后,未在合适的时间或无法正确释放该段内存,而导致的。内存泄漏通常需要程序员通过分析程序源代码来识别和修复。

  内存泄漏的危害:

  在某些情况下,内存泄漏可能并不严重,或者能够被常规手段检测出来。然而,如果内存泄漏持续存在并导致大量可用内存被分配掉,可能会导致计算机性能显著下降,甚至导致全部或部分设备停止正常工作,或者应用程序崩溃。如果长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

  
  内存泄漏的示例:

void MemoryLeaks()
{
   // 1.内存申请了忘记释放
  int* p1 = (int*)malloc(sizeof(int));
  int* p2 = new int;
  
  // 2.异常安全问题
  int* p3 = new int[10];
  
  Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
  
  delete[] p3;
}

  
  如何避免内存泄漏:

  (1)合理使用动态内存:尽量避免使用动态内存分配,因为使用动态内存分配后需要手动释放内存,否则会造成内存泄漏。如果必须使用动态内存分配,应该确保在使用完毕后及时释放内存。

  (2)及时释放内存:在使用完动态分配的内存后,必须及时释放它们,以避免内存泄漏。在C++中,可以在析构函数中释放内存;在C中,可以使用free()函数来释放内存。

  (3)使用智能指针:智能指针是一种能够自动管理内存的机制,可以避免由于忘记释放内存而导致的内存泄漏。 C++11标准库提供了多种类型的智能指针,如unique_ptr、shared_ptr和weak_ptr,可以根据具体情况选择合适的智能指针类型来管理内存。

  (4)尽量避免循环引用:如果两个对象之间存在相互引用关系,并且没有任何其他对象或机制来打破循环引用,那么就会导致内存泄漏。因此,在设计程序时应该尽可能避免循环引用,或者使用其他机制来打破循环引用。

  (5)使用垃圾回收机制:一些编程语言提供了垃圾回收机制,可以自动检测并回收不再使用的内存。这些语言包括Java、Python和JavaScript等。如果使用这些语言,可以充分利用它们的垃圾回收机制来避免内存泄漏。

  (6)定期检查程序:定期检查程序是否存在内存泄漏是一种有效的手段。可以使用一些工具来检测程序中的内存泄漏, 例如Valgrind和AddressSanitizer等。这些工具可以帮助发现程序中的内存泄漏问题,并提供修复建议。

            

7.2智能指针的概念

  智能指针是一种C++中的对象,它用于管理动态分配的内存,并自动释放内存以避免内存泄漏。智能指针的工作原理是,当智能指针超出其作用域或被销毁时,它会自动释放它所管理的内存。

  智能指针的常见类型包括unique_ptr、shared_ptr和weak_ptr。 unique_ptr表示只有一个指针指向动态分配的内存,当unique_ptr被销毁时,它所管理的内存会被自动释放。shared_ptr表示多个指针可以指向同一块动态分配的内存,当最后一个指向该内存的shared_ptr被销毁时,它所管理的内存会被自动释放。weak_ptr是shared_ptr的一个别名,它不持有动态分配的内存的任何所有权,而是用于与其他智能指针一起使用,以避免循环引用。

  下面是一个简单的unique_ptr使用的例子:

  在这个例子中,我们首先定义了一个名为MyStruct的结构体,它有一个整数值。接下来,在main函数中,我们使用std::unique_ptr< MyStruct >类型定义了一个智能指针对象ptr,并使用new操作符分配了一个新的MyStruct对象,将它的指针赋给了ptr。我们可以通过ptr->value来访问MyStruct对象的值。最后,当main函数返回时,ptr将自动释放它所管理的内存,避免了内存泄漏。

#include <iostream>  
#include <memory>  
  
struct MyStruct {  
    int value;  
    MyStruct(int v) : value(v) {}  
};  
  
int main() {  
    std::unique_ptr<MyStruct> ptr(new MyStruct(10));  
    std::cout << "Value: " << ptr->value << std::endl;  
    return 0;  
}

            

7.3智能指针的使用

   RAII:

  RAII是资源获取就是初始化的简称,是C++语言的一种管理资源、避免泄漏的惯用法。

  RAII的核心思想是,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理。这样可以保证资源的正确初始化和释放,防止内存泄漏。

  利用C++构造的对象最终会被销毁的原则,当对象被销毁时,会自动调用其析构函数,释放对象生命期内控制的资源。

  RAII有两个好处:

  (1)不需要显式地释放资源;

  (2)采用这种方式,对象所需的资源在其生命期内始终保持有效。

// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr 
{
public:
	SmartPtr(T* ptr = nullptr)
			: _ptr(ptr)
		{}

	~SmartPtr()
	{
		if(_ptr)
		delete _ptr;
	}

private:
	T* _ptr;
};

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
	throw invalid_argument("除0错误");
	return a / b;
}

void Func()
{
	ShardPtr<int> sp1(new int);
	ShardPtr<int> sp2(new int);
	cout << div() << endl;
}

int main()
{
	try 
	{
		Func();
	}
	catch(const exception& e)
	{
		cout<<e.what()<<endl;
	}
	return 0;
}

  

  上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。

  
  总结一下智能指针的原理:

  (1)RAII特性

  (2)重载operator*和opertaor->,具有像指针一样的行为

template<class T>
class SmartPtr 
{
public:
	SmartPtr(T* ptr = nullptr)
    	 : _ptr(ptr)
	 {}
	 
	~SmartPtr()
 	{
     	if(_ptr)
         	delete _ptr;
 	}
 	
	T& operator*() {return *_ptr;}

	T* operator->() {return _ptr;}

private:
	T* _ptr;
};

struct Date
{
	int _year;
	int _month;
	int _day;
};

int main()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 10
	cout<<*sp1<<endl;
	SmartPtr<int> sparray(new Date);
	// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
	// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
	sparray->_year = 2018;
	sparray->_month = 1;
	sparray->_day = 1;
}

  

7.3.1 auto_ptr

std::auto_ptr

  C++98版本的库中就提供了auto_ptr的智能指针。

  注意:auto_ptr被认为是一个失败的设计:

  (1)所有权的转移不清晰:auto_ptr在构造时获取对象的所有权,但可以通过赋值操作将所有权转移给另一个auto_ptr。这样会造成原来的auto_ptr指针悬空,程序直接崩溃。

  (2)无法在STL容器中使用:auto_ptr的拷贝构造函数会进行所有权的转移,这使得它难以被用在STL容器中。因为在容器中添加元素时,会涉及到拷贝操作,这可能会导致所有权的转移,从而使得容器中的元素所指向的对象被释放,引起程序错误。

  (3)nullptr的问题:auto_ptr无法安全地处理nullptr。如果你试图让一个auto_ptr指向一个nullptr,它会在析构时释放该指针,即使该指针从未被分配过内存。

  (4)不支持自定义删除器:auto_ptr只能使用默认的delete操作符来释放内存,无法使用自定义的删除器。这限制了auto_ptr的灵活性。基于以上原因,现代C++编程中更推荐使用unique_ptr、shared_ptr和weak_ptr来替代auto_ptr。

  auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原理:

template<class T>
class auto_ptr
{
public:
	auto_ptr(T* ptr)
		:_ptr(ptr)
	{}

	~auto_ptr()
	{
		cout << "delete:" << _ptr << endl;
		delete _ptr;
	}

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

	T* operator->()
	{
		return _ptr;
	}

	//管理权转移
	auto_ptr(auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}

private:
	T* _ptr;
};

  

7.3.2 unique_ptr

std::unique_ptr

  unique_ptr是C++11标准库中的一种智能指针,它持有对对象的独有权,即两个unique_ptr不能指向同一个对象,不能进行复制操作,只能进行移动操作。 当unique_ptr超出其作用域或被销毁时,它所管理的内存会被自动释放,有效避免了内存泄漏问题。unique_ptr是一种方便、安全、自动管理内存的机制,可以有效避免内存泄漏问题。

  unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份unique_ptr来了解它的原理:

template<class T>
class unique_ptr
{
public:
	unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	~unique_ptr()
	{
		cout << "delete:" << _ptr << endl;
		delete _ptr;
	}

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

	T* operator->()
	{
		return _ptr;
	}

	//禁止拷贝
	unique_ptr(unique_ptr<T>& ap) = delete;
	unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

private:
	T* _ptr;
};

  

7.3.3 shared_ptr

std::shared_ptr

  shared_ptr是一种C++中的智能指针,它可以记录有多少个shared_ptrs指向一个对象。当最后一个这样的指针被销毁时,也就是最后一个指向对象的shared_ptr被销毁时,该对象会被自动删除,这在非环形数据结构中防止资源泄露很有帮助。

  它与unique_ptr不同,shared_ptr允许多个指针指向同一个对象,并共同拥有该对象的所有权。当最后一个shared_ptr被销毁时,它所指向的对象也会被自动删除。

  
  shared_ptr的实现原理:

  shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。

  如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源; 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

  shared_ptr的实现原理:使用了引用计数,下面简化模拟实现了一份shared_ptr来了解它的原理:

template<class T>
class shared_ptr
{
public:
	// RAII
	shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}

	~shared_ptr()
	{
		if (--(*_pcount) == 0)
		{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
			delete _pcount;
		}
	}

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

	T* operator->()
	{
		return _ptr;
	}

	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
	{
		++(*_pcount);
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (_ptr == sp._ptr)
			return *this;

		if (--(*_pcount) == 0)
		{
			delete _ptr;
			delete _pcount;
		}

		_ptr = sp._ptr;
		_pcount = sp._pcount;
		++(*_pcount);

		return *this;
	}

	int use_count() const
	{
		return *_pcount;
	}

	T* get() const
	{
		return _ptr;
	}

private:
	T* _ptr;
	int* _pcount;
};

  

7.3.4 weak_ptr

std::weak_ptr

  weak_ptr是C++中为了配合shared_ptr解决循环引用问题而引入的一种智能指针。

  weak_ptr可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起shared_ptr引用记数的增加或减少。 weak_ptr的一个重要用途是通过lock获得this指针的shared_ptr,使对象自己能够生产shared_ptr来管理自己。总的来说,weak_ptr主要用于配合shared_ptr,以避免内存泄漏。

  下面简化模拟实现了一份weak_ptr来了解它的原理:

template<class T>
class weak_ptr
{
public:
	weak_ptr()
		:_ptr(nullptr)
	{}

	weak_ptr(const shared_ptr<T>& sp)
		:_ptr(sp.get())
	{}

	weak_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		_ptr = sp.get();
		return *this;
	}

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

	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1084555.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

矿区井下智慧用电安全监测解决方案

一、背景 矿区井下作业具有复杂的环境和较高的危险性&#xff0c;对于用电安全的要求尤为严格。传统的管理模式和监测方法往往无法实时、准确地掌握井下用电情况&#xff0c;对安全隐患的排查与预防存在一定局限性。因此&#xff0c;引入智慧用电安全监测解决方案&#xff…

MySQL系列第一篇入门

1.什么是关系型数据库呢&#xff1f; RDBMS 是一种结构化数据存储系统&#xff0c;使用表格间的关系来存储和操作数据。 在关系型数据库中&#xff0c;数据以行和列的形式存储&#xff0c;其中每一行表示一个关系或实体&#xff0c;每一列表示该实体的某个属性或特征 关系型数…

干洗店管理软件,家政洗衣洗鞋店上门服务小程序

干洗店管理系统&#xff0c;洗鞋店小程序下单&#xff0c;收衣收鞋预约&#xff1b; 洗衣店洗鞋店收衣管理APP&#xff0c;根据会员所属地区或门店&#xff0c;自动把信息派送到收衣员工的APP上。 洗衣店洗鞋店小程序&#xff0c;支持通过预约单&#xff0c;生成会员收衣单据或…

Cisdem Video Player for mac(高清视频播放器) v5.6.0中文版

Cisdem Video Player mac是一款功能强大的视频播放器&#xff0c;适用于 macOS 平台。它可用于播放不同格式的视频文件&#xff0c;并具有一些实用的特性和功能。 Cisdem Video Player mac 中文版软件特点 多格式支持&#xff1a;Cisdem Video Player 支持几乎所有常见的视频格…

Flask框架配置celery-[1]:flask工厂模式集成使用celery,可在异步任务中使用flask应用上下文,即拿即用,无需更多配置

一、概述 1、celery框架和flask框架在运行时&#xff0c;是在不同的进程中&#xff0c;资源是独占的。 2、celery异步任务如果想使用flask中的功能&#xff0c;如orm&#xff0c;是需要在flask应用上下文管理器中执行orm操作的 3、使用celery是需要使用到中间件的&#xff0…

2023年【煤气】试题及解析及煤气复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 煤气试题及解析考前必练&#xff01;安全生产模拟考试一点通每个月更新煤气复审模拟考试题目及答案&#xff01;多做几遍&#xff0c;其实通过煤气理论考试很简单。 1、【单选题】100mm以上的煤气管道着火,( )一下把煤…

vue3封装分页组件

1.新建Pagination文件以及该文件夹下新建index.vue 2.在index.vue文件中编写一下代码 <template><div :class"{ hidden: hidden }" class"pagination-container"><el-pagination:background"background"v-model:current-page&qu…

全国快递查询接口,快递,全球快递,配送,物流管理,物流数据,电子商务

一、接口介绍 支持国内外1500快递物流公司的物流跟踪服务&#xff0c;包括顺丰、圆通、申通、中通、韵达等主流快递公司。同时&#xff0c;支持单号识别快递物流公司、按次与按单计费、物流轨迹返回等功能&#xff0c;以满足企业对快递物流查询多维度的需求。 二、使用案例截…

IDEA中.gitignore配置不生效的解决方案

一、创建项目 二、执行以下Git命令 git rm -r --cached . git add . git commit -m "update .gitignore"

接口测试的几种方法

其实无论用那种测试方法&#xff0c;接口测试的原理是通过测试程序模拟客户端向服务器发送请求报文&#xff0c;服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给客户端&#xff0c;客户端接收应答报文这一个过程。 方法一、用LoadRunner实现接口测试 大家都知…

风光十几年的MIUI要无了,小米自研MIOS即将就位

今年8月小米新品发布会上&#xff0c;K60 至尊版亮相的同时带来了下一代系统&#xff08;或者说UI &#xff09;的名字&#xff1a;MIUI 15 。 好家伙不知不觉已经要更到 15 了&#xff0c;大家用过几个版本呢&#xff1f; 回想起 MIUI V1 内测发布、MIUI V5 全民刷机、MIUI 8…

显卡、SSD二合一,华硕这波「技术革命」给我看傻了

通常来讲&#xff0c;高端、低端主板除了芯片组、用料、供电规格等差异&#xff0c;在接口拓展性方面也存在着明显分级。 最常用的 USB 接口倒还好&#xff0c;即便目前最入门级主板基本也能保证至少 4 个&#xff1b; 加上机箱前面板拓展 USB 口&#xff0c;保证一般用户需求…

Android studio安装详细教程

Android studio安装详细教程 文章目录 Android studio安装详细教程一、下载Android studio二、安装Android Studio三、启动Android Studio 一、下载Android studio Android studio安装的前提是必须保证安装了jdk1.8版本以上 1、打开android studio的官网&#xff1a;Download…

3D模型格式转换工具HOOPS Exchange:模型数据自由导入和导出

HOOPS Exchange是一套高性能软件库&#xff0c;可以为软件开发人员提供导入和导出3D文件格式的能力。HOOPS Exchange导入3D数据后&#xff0c;会将3D数据转换为PRC格式存放到内存中&#xff0c;最后导出成为其他3D格式。&#xff08;点击申请HOOPS Exchange免费试用&#xff09…

互动设计:深入了解用户体验的关键

交互是人与计算机系统之间的互动过程。在计算机领域中&#xff0c;交互是人机交互技术的核心内容之一。交互设计是一种基于人类行为科学、心理学、人体工程学等领域的专业设计&#xff0c;目的是创造用户友好的、易于使用的计算机软件、网络、移动应用等。交互的本质在于用户的…

JUC并发编程:Monitor和对象结构

JUC并发编程&#xff1a;Monitor和对象结构 1. Monitor1.1 对象的结构1.1.1 MarkWord1.1.2 Klass Word1.1.3 数组长度1.1.4 &#x1f330; 1. Monitor Monitor官方文档 我们可以把Monitor理解为一个同步工具&#xff0c;也可以认为是一种同步机制。它通常被描述为一个对象&…

零基础Linux_15(基础IO_文件)软硬链接+动静态库详解

目录 1. 软硬链接 1.1 创建软链接 1.2 创建硬链接 1.3 硬链接数和unlink 2. 动静态库 2.1 制作静态库 2.2 查看和打包静态库 2.3 使用静态库 2.3.1 安装在默认搜索路径 2.3.2 告知路径库路径库名 2.4 制作动态库 2.5 使用动态库 2.5.1 安装在默认搜索路径 2.5.2 …

轻松虚拟gps定位 AnyGo中文 for mac

AnyGo是一款旨在帮助用户模拟和改变iPhone的GPS位置&#xff0c;以实现虚拟定位的功能。该软件适用于iOS设备&#xff0c;并提供了一系列强大的功能&#xff0c;让用户能够自由地模拟不同地理位置&#xff0c;以满足各种需求。 以下是AnyGo可能提供的一些主要功能和特点&#…

设置全局滚动条样式

需求&#xff1a;统一滚动条样式&#xff0c;不需要每次都要在页面一个一个的设置&#xff0c;整个项目只要内容超出了显示滚动条了就会是自己设置的那个滚动条样式显示 1.效果 2. 创建scroll.scss文件 滚动条样式如下&#xff0c;可以自行更改 /* // 滚动条样式重写-chrome …

新闻api接口,新闻资讯,社交媒体,体育赛事,全国热门带正文新闻查询API接口

一、接口介绍 解决同一类新闻在不同平台上的内容获取问题&#xff0c;在归档主流新闻平台的内容数据基础上&#xff0c;对外提供统一的调用方式来完成实时、最新的相关新闻的获取&#xff0c;极大方便各类企业在自有软件中集成新闻内容的功能。支持200余个新闻大站&#xff0c;…