当前位置: 首页 > news >正文

【C++详解】——智能指针

目录

为什么需要智能指针

抛异常引发内存泄漏

内存泄漏

什么是内存泄漏,内存泄漏的危害

 内存泄漏分类

检测内存泄漏常用工具

如何避免内存泄漏

智能指针的使用及原理

RAII

智能指针的原理

各类智能指针介绍

auto_ptr

unique_ptr

shared_ptr

weak_ptr

定制删除器

 


为什么需要智能指针

抛异常引发内存泄漏

对于下面这段代码,存在什么问题?

int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}
void Func()
{// 1、如果p1这里new 抛异常会如何?// 2、如果p2这里new 抛异常会如何?// 3、如果div调用这里又会抛异常会如何?int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}
int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}

分析上述代码,如果在p1处抛异常,代码直接跳转到main函数中捕获异常,不会造成资源泄漏。但是如果在p2被new时,或者调用div()函数时抛异常,代码跳转到main()函数,两个delete语句会被跳过,导致内存泄漏。

内存泄漏

什么是内存泄漏,内存泄漏的危害

内存泄漏

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

内存泄漏的危害

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

常见的内存泄漏 

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;
}

 内存泄漏分类

C/C++程序中一般我们关心两种方面的内存泄漏:

  • 堆内存泄漏(Heap leak):堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的free或者delete删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
  • 系统资源泄漏:指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

检测内存泄漏常用工具

以下几种工具可以用来帮助检测是否存在内存泄漏

  • 在Linux下内存泄漏检测:Memory Debuggers - eLinux.org
  • 在windows下使用第三方工具检测:VLD(Visual LeakDetector)内存泄露库
  • 其他检测工具比较:内存泄露检测工具比较 - 默默淡然 - 博客园 (cnblogs.com)

如何避免内存泄漏

  • 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。这个理想状态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保证。
  • 采用RAII思想或者智能指针来管理资源(后面将要介绍的智能指针即采用RAII思想)
  • 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项
  • 出问题了使用内存泄漏工具检测

【总结】
内存泄漏非常常见,解决方案分为两种:

1、事前预防型:如智能指针等。 2、事后查错型:“如泄漏检测工具。


智能指针的使用及原理

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后
对象析构的时候释放资源
。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if (_ptr)delete _ptr;}
private:T* _ptr;
};

智能指针的原理

上述的SmartPtr还不能将其称为智能指针,上述模拟实习的智能指针只用到了RAII的思想,即构造对象时获取资源、析构对象时回收资源,但是它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。

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;
};

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

  • RAII特性
  • 重载operator*和opertaor->,具有像指针一样的行为

各类智能指针介绍

auto_ptr

早在C++98中就已经出现了智能指针的概念,同时库中也提供了auto_ptr的智能指针。当时采用的是管理权转移的思想,下面简化模拟实现了一份auto_ptr来了解它的原理。

template<class T>
class auto_ptr
{
public://RAII//保存资源auto_ptr(T* ptr):_ptr(ptr){}//释放资源~auto_ptr(){delete _ptr;}//auto_ptr采用资源管理权转移 -- 但是会导致对象悬空auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){sp._ptr = nullptr;}//智能指针要有指针的功能T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t pos){return _ptr[pos];}
private:T* _ptr;
};

auto_ptr采用了RAII思想,可以解决抛异常引发的内存泄漏问题,但同时又造成了新的问题。auto_ptr在实现拷贝构造函数时采用了管理权转移的方式。


相信大伙一眼就能看出这种实现思想有很坑的一点,用一个旧的指针去构造一个新的指针之后,只有新的指针指向了内容,旧的指针指向了空,但我们仍可能去解引用旧的指针啊。例如下面的例子一定会引发错误。

void test_auto()
{auto_ptr<int> ap1(new int(1));auto_ptr<int> ap2(ap1);*ap1 = 1; // 管理权转移以后导致ap1悬空,不能访问*ap2 = 1;
}


 

auto_ptr这一问题也是被骂了很长时间,C++标准委员会也是直接表明不要使用auto_ptr。

unique_ptr

unique_ptr是C++11标准库提供的一个靠谱的智能指针,unique_ptr既体现了RAII的思想,同时也没有auto_ptr的问题,因为它直接就没有拷贝构造函数。

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

template<class T>
class unique_ptr
{
public://RAII//保存资源unique_ptr(T* ptr):_ptr(ptr){}//释放资源~auto_ptr(){delete _ptr;}//智能指针要有指针的功能T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t pos){return _ptr[pos];}//C++11:可以用=delete直接封死拷贝构造和赋值构造unique_ptr(const unique_ptr<T>& up) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;private://防拷贝// 拷贝构造和赋值是默认成员函数,我们不写会自动生成,所以我们不需写// C++98思路:只声明不实现,但是用的人可能会在外面强行定义,所以再加一条,声明为私有//unique_ptr(const unique_ptr<T>& up);//unique_ptr<T>& operator=(const unique_ptr<T>& up);T* _ptr;
};

shared_ptr

C++11中除了提供了unique_ptr还提供更靠谱的并且支持拷贝的shared_ptr。

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  • shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
    享。
  • 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减
    一。
  •  如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源。
  • 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对
    象就成野指针了。

实现拷贝构造时采用引用计数的方式,在实现赋值重载时,要注意等号左边的指针指向的内容是否需要被释放。

情况一:

 

情况二:

 

shared_ptr具体实现代码:

template<class T>
class shared_ptr {
public://RAII//保存资源shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)){}//释放资源~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){AddCount();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (sp._ptr != _ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;AddCount();}return *this;}void Release(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}void AddCount(){++(*_pcount);}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;int* _pcount;
};

上述的代码已经可以满足一般的应用场景,但是设计到多线程操作时,上面的shared_ptr存在线程安全的问题,所以我们最好还是实现通过加锁实现一个线程安全的版本。

template<class T>
class shared_ptr {
public://RAII//保存资源shared_ptr(T* ptr):_ptr(ptr),_pcount(new int(1)),_pmtx(new mutex){}//释放资源~shared_ptr(){Release();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount),_pmtx(sp._pmtx){AddCount();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (sp._ptr != _ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;AddCount();}return *this;}void Release(){_pmtx->lock();if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_pmtx->unlock();}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}int use_count(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;
};

weak_ptr

循环引用的情况:

比如上面这个双向链表,我们一般就是这样使用,但是因为抛异常可能导致内存泄漏的问题,于是我们改用智能指针。n1和n2不构成循环时可以正常使用智能指针。

n1和n2构成循环引用时,智能指针无法释放空间,造成内存泄漏。

原因是

  • node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
  • node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
  •  node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
  • 也就是说_next析构了,node2就释放了。
  • 也就是说_prev析构了,node1就释放了。
  • 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放。

解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
weak_ptr原理就是,node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和
_prev不会增加node1和node2的引用计数。

【注意】

weak_ptr不是常规的智能指针,不支持RAII ,但是它支持像指针一样

专门设计出来,辅助解决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()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}private:T* _ptr;};

shared_ptr和weak_ptr配合使用就可以解决循环引用的问题了。 

定制删除器

如果不是new出来的对象如何通过智能指针管理呢?其实shared_ptr设计了一个删除器来解决这
个问题。

// 仿函数的删除器template<class T>struct FreeFunc {void operator()(T* ptr){cout << "free:" << ptr << endl;free(ptr);}};template<class T>struct DeleteArrayFunc {void operator()(T* ptr){cout << "delete[]" << ptr << endl;delete[] ptr;}};

定制删除器版本shared_ptr

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}template<class D>shared_ptr(T* ptr, D del): _ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}~shared_ptr(){Release();}void Release(){_pmtx->lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr){//cout << "delete:" << _ptr << endl;//delete _ptr;// 删除器进行删除_del(_ptr);}delete _pcount;deleteFlag = true;}_pmtx->unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}// sp1 = sp4// sp1 = sp1;// sp1 = sp2;shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;AddCount();}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}int use_count(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;// 包装器function<void(T*)> _del = [](T* ptr){cout << "lambda delete:" << ptr << endl;delete ptr;};
};

运行结果: 

相关文章:

【C++详解】——智能指针

目录 为什么需要智能指针 抛异常引发内存泄漏 内存泄漏 什么是内存泄漏&#xff0c;内存泄漏的危害 内存泄漏分类 检测内存泄漏常用工具 如何避免内存泄漏 智能指针的使用及原理 RAII 智能指针的原理 各类智能指针介绍 auto_ptr unique_ptr shared_ptr weak_ptr …...

Jmeter接口/性能测试,Jmeter使用教程(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、线程组 线程组…...

59,综合案例-演讲比赛流程管理系统

演讲比赛流程管理系统 59.1案例描述59.1.1比赛规则59.1.2程序功能 59.2创建管理类59.3菜单功能59.3.1添加成员函数59.3.2菜单功能实现 59.4退出功能59.4.1提供功能接口59.4.2实现退出功能 59.5演讲比赛功能59.5.1创建选手类59.5.2比赛59.5.2.1成员属性添加59.5.2.2初始化属性59…...

前端JS 展示上传图片缩略图(本地图片读取)

需求&#xff1a; 点击上传图片按钮&#xff0c;选择图片以后&#xff0c;不请求后端接口&#xff0c;直接将图片展示在缩略图中。 解决方案&#xff1a; 使用 FileReader 和 FileReader 中的 readAsDataURL 方法。 第一步 从input[type“file”] (上传文件标签) 里面拿到fil…...

Vue中$route和$router的区别

$router&#xff1a;用来操作路由&#xff0c;$route&#xff1a;用来获取路由信息 $router其实就是VueRouer的实例&#xff0c;对象包括了vue-router使用的实例方法&#xff0c;还有实例属性&#xff0c;我们可以理解为$router有一个设置的含义&#xff0c;比如设置当前的跳转…...

基于多任务学习卷积神经网络的皮肤损伤联合分割与分类

文章目录 Joint segmentation and classification of skin lesions via a multi-task learning convolutional neural network摘要本文方法实验结果 Joint segmentation and classification of skin lesions via a multi-task learning convolutional neural network 摘要 在…...

串口环形缓冲区

文章目录 一、串口环形缓冲区概念二、STC12例程&#xff08;1&#xff09;环形串口缓冲区结构体&#xff08;2&#xff09;串口环形缓冲区存和取数据&#xff08;3&#xff09;完整工程demo 一、串口环形缓冲区概念 串口环形缓冲区应用于嵌入式、物联网开发中处理接收串口数据…...

【腾讯云 Cloud Studio 实战训练营】基于Cloud Studio完成简易通讯录

目录 &#x1f506;Cloud Studio 简介 操作步骤 1.登录 2.创建工作空间 3.初始界面 4.开发空间 5.保存自定义模板 &#x1f506;简易通讯录 1.实验要求 2.操作环境 3.源代码介绍 3.1 定义通讯录类 3.2 定义通讯录列表 3.3 添加联系人功能 3.4 修改联系人 3.5 …...

【技术积累】Vue.js中的核心知识

Vue的生命周期 Vue中的生命周期是指组件从创建到销毁的整个过程中&#xff0c;会触发一系列的钩子函数 Vue2中的生命周期 Vue2中的生命周期钩子函数是在组件的不同阶段执行的特定函数。这些钩子函数允许开发者在组件的不同生命周期阶段执行自定义的逻辑。 Vue2中的生命周期钩…...

flutter开发实战-显示本地图片网络图片及缓存目录图片

flutter开发实战-显示本地图片网络图片及缓存目录图片 在最近开发中碰到了需要显示缓存目录图片&#xff0c;这里顺便整理一下&#xff0c;显示本地图片、网络图片、缓存目录图片的方法。 一、工程本地图片显示 1 在项目根目录下创建名为 images文件夹&#xff0c;也可以将i…...

面对未来的算法备案法规:企业需要做哪些准备?

在信息时代&#xff0c;算法已经成为我们生活的一部分&#xff0c;涵盖了诸如搜索引擎、社交媒体、电子商务、广告投放等各个方面。然而&#xff0c;随着算法的广泛应用&#xff0c;其带来的问题也日益凸显。这引发了全球范围内的关注&#xff0c;未来的算法备案法规正在酝酿之…...

iptables的备份和还原

iptables的备份和还原 1、写在命令行当中的都是临时设置 2、把规则配置写在服务的文件当中&#xff0c;形成永久有效 备份&#xff1a;把iptables里面所有的配置都保存在/opt/ky30.bak中 iptables-save > /opt/ky30.bak 例&#xff1a; 默认配置文件在/etc/sysconfig/ip…...

easyUI框架学习

文章目录 一、前言二、引入使用easyUI 三、用法3.1 Dialog&#xff08;对话框窗口&#xff09;3.1.1 示例13.1.2 示例2 3.2 Layout&#xff08;布局&#xff09;3.2.1 示例1——通过标签创建布局3.2.2 示例2—— 创建嵌套布局 3.3 DateBox&#xff08;日期输入框&#xff09;3.…...

加入气压计模组,星斗3号将实现快速三维定位

随着信息技术的飞速发展,人们的生活正在发生巨大改变,人们对基于位置服务的需求越来越迫切,尤其是室内位置服务。 室外定位系统中BDS系统、GPS系统等受室内复杂环境条件限制精度大幅下降甚至失效,难以在室内定位中发挥作用,而室内二维定位技术缺乏高程信息,也难以满足人们对室…...

华为HCIP第二节-------------------------ISIS

IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP&#xff08;ConnectionLessNetwork Protocol&…...

在Mac系统下搭建Selenium环境并驱动Chrome浏览器

本文带领那些使用Mac的童鞋们实现Selenium驱动Chrome浏览器&#xff0c;虽然会有坑&#xff0c;但是我们可以凭借敏捷的身手躲过。下面就开始吧&#xff1a; 安装selenium 打开终端 ->pip安装&#xff08;安装命令&#xff1a;pip3 install selenium&#xff09; 安装浏览…...

通过RPM方式安装,升级,卸载,以及配置使用MySQL

通过RPM方式安装&#xff0c;升级&#xff0c;卸载&#xff0c;以及配置使用MySQL 一、下载 MySQL是一种开源的关系数据库管理系统&#xff0c;被广泛应用于各种业务应用中。本文将讲解如何下载和安装MySQL的rpm安装包。 下载rmp安装包有多种方式&#xff1a; 1、官网下载 …...

六边形架构和分层架构的区别?

六边形架构和分层架构是什么&#xff1f; 六边形架构&#xff08;Hexagonal Architecture&#xff09;和分层架构&#xff08;Layered Architecture&#xff09;是两种常见的软件架构模式。六边形架构强调将核心业务逻辑与外部依赖解耦&#xff0c;通过接口与外部世界进行通信。…...

一封来自Java学姐的信

黑马JavaEE学科学姐想对学弟学妹们说&#xff1a;勤学如春起之苗&#xff0c;不见其增&#xff0c;日有所长。 辍学如磨刀之石&#xff0c;不见其损&#xff0c;日有所亏。 学科 | JavaEE 校区 | 太原 亲爱的学弟学妹们&#xff0c;在学校“混日子”的时间很快就过去了&…...

Mybatis增强版MyBatis-Flex简介

Mybatis增强版&#xff1a;Mybatis-Plus(使用的最多&#xff0c;老牌Mybatis增强框架&#xff0c;2016年开源)、Fluent-MyBatis(阿里云开发的Mybatis增强框架&#xff0c;来自阿里云.云效产品团队)、Mybatis-Flex。 Flex英文单词意思是灵活&#xff0c;Mybatis-Flex官方文档中多…...

MFC第二十一天 CS架构多页面开发与数据交互、CImageList图像列表介绍 、CListCtrl-SetItem设置列表项的方法

文章目录 CImageList图像列表介绍CListCtrl图标的原理CListCtrl列表图标设置CListCtrl-SetItem设置列表项的方法 CS架构多页面开发与数据交互添加用户实现向导多页数据交互pch.hCLientXq.h CAppCPage1.hCPage1.cppCPage2.hCPage2.cppCWorkerDlg .hCWorkerDlg.cpp 多页数据修改C…...

spring boot--自动化注入组件原理、内嵌tomcat-1

前言 我们知道开发spring boot项目&#xff0c;在启动类上添加注解SpringBootApplication &#xff0c;然后引入要自动注入的组件依赖&#xff0c;然后现application.properties中加上相应配置就可以自动注入这个组件&#xff0c;那么下面看看自动注入组件是如何实现的 一、S…...

短视频矩阵系统源码---开发技术源码能力

短视频矩阵系统开发涉及到多个领域的技术&#xff0c;包括视频编解码技术、大数据处理技术、音视频传输技术、电子商务及支付技术等。因此&#xff0c;短视频矩阵系统开发人员需要具备扎实的计算机基础知识、出色的编程能力、熟练掌握多种开发工具和框架&#xff0c;并掌握音视…...

可观测之调用链Skywalking

简介 分布式系统的应用程序性能监视工具&#xff0c;专为微服务、云原生架构和基于容器&#xff08;Docker、K8s、Mesos&#xff09;架构而设计。提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。 多种监控手段。可以通过语言探针和 service mesh 获得监控…...

linux上适用的反汇编调试软件(对标od)

ubuntu下类似于od软件 经过搜索&#xff0c;在Ubuntu上选用edb-debugger进行动态调试&#xff0c; 下载链接: https://github.com/eteran/edb-debugger 但是依赖反汇编引擎: https://github.com/capstone-engine/capstone 安装 先安装capstone 先下载release的版本&#xf…...

基于高斯混合模型聚类的风电场短期功率预测方法(Pythonmatlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 2.1 Python 2.2 Matlab &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、数据、文章讲解 &#x1f4a5;1 概述 文献来源&#xff1a; 摘要&#xff1a;对任意来流条件下的风电场发电功率进行准确预测,是提高电网对风电…...

【深入了解pytorch】PyTorch循环神经网络(RNN)

【深入了解pytorch】PyTorch循环神经网络(RNN) PyTorch循环神经网络(RNN):概念、工作原理与常见变体循环神经网络概念和工作原理RNN的结构RNN的工作原理LSTM(长短期记忆网络)LSTM的结构LSTM的工作原理GRU(门控循环单元)GRU的结构GRU的工作原理在PyTorch中实现RNN、LST…...

电商运营的方法

1、以后干,不如现在干 1.1 做代理,搞研发 1.2 自建店铺,去看其他店铺的设计样板 1.3 记住网店挣钱三要点:装修,物流,产品资源 1.4 记住你的职责,让别人明白怎么做,仔细看资料,搞清楚细节 2、如何打开机器人 3.设置自动回复 Ctrl + tab 4.如何做基础销量,做一个刷…...

Swift 如何确定 scrollView 已经滑动结束

在 iOS 的 UIScrollView 中&#xff0c;你可以通过实现 UIScrollViewDelegate 的方法来检测滑动结束事件。具体来说&#xff0c;你可以实现以下方法&#xff1a; func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {if !decelerat…...

html学习2(css、图像)

1、层叠样式表css是为了更好的渲染html元素而引入的。 2、css添加到html的方式&#xff0c;最好的方式是通过外部引用CSS文件 内联样式- 在HTML元素中使用"style" 属性&#xff0c;当特殊的样式需要应用到个别元素时&#xff0c;就可以使用内联样式。 使用内联样式…...