【C++学习笔记】RAII思想——智能指针
智能指针
- 1 内存泄漏问题
- 2 RAII(Resource Acquisition Is Initialization)
- 2.1 使用RAII思想设计的SmartPtr类
- 2.2 智能指针的原理
- 2.3 小总结智能指针原理
- 3 智能指针的拷贝问题
- 3.1 std::auto_ptr
- 3.2 std::unique_ptr
- 3.3 std::shared_ptr
- 3.3.1 拷贝构造函数
- 3.3.2 赋值函数
- 3.4 std::shared_ptr的线程安全问题
- 3.4 .1智能指针引用计数操作加锁
- 3.4.2 外界内存资源是非线程安全的
- 3.5 std::shared_ptr 引发的循环引用问题
- 4 std::weak_ptr 解决循环引用问题
- 4.1 weak_ptr解决循环引用的原理
- 4.2 简易版weak_ptr的实现
- 5 删除器
- 6 C++11和boost中智能指针的关系
- 7 总结
1 内存泄漏问题
在讲智能指针之前,我们先来看下面这段代码有什么问题?
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;
}
这段代码有内存泄漏问题。
1.对p1来说,如果new空间失败了,它会抛出异常直接跳转到main函数进行处理。所以,如果只是p1抛异常不会造成内存泄漏问题。
2.如果p1申请空间成功,对p2来说,如果new空间失败了,它也会抛出异常直接跳转到main函数进行处理,但是p1的内存空间并没有得到释放,会造成内存泄漏问题。
未使用智能指针的解决方式如下:
int* p1 = new int;int* p2 = nullptr;try{p2 = new int;}catch(...)//表示捕获任意异常{delete p1;//p2抛异常的话,在这里释放掉p1占的内存throw;//重新抛出异常给main函数}cout << div() << endl;delete p1;delete p2;
3.假如p1,p2都申请内存成功,并未抛出异常。但是div()函数抛出了异常,直接跳转到main函数,会导致p1和p2的内存空间都没有释放,造成内存泄漏。
未使用智能指针的解决方式如下:
int* p1 = new int;int* p2 = nullptr;try{p2 = new int;}catch(...)//表示捕获任意异常{delete p1;throw;}try {cout << div() << endl;}catch (...){delete p1;delete p2;throw;}delete p1;delete p2;
}
以上的的解决方法虽然解决了抛异常可能会造成的内存泄漏问题,但是从代码上来说太过繁琐,不够优雅。
可以用RAII的思想来解决问题。
2 RAII(Resource Acquisition Is Initialization)
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
1.不需要显式地释放资源。
2.采用这种方式,对象所需的资源在其生命期内始终保持有效。
2.1 使用RAII思想设计的SmartPtr类
首先先设计一个SmartPtr类,类中有构造函数和析构函数,如果想申请空间和销毁空间,分别交给构造函数和析构函数即可。
template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;}
private:T* _ptr;
};
完整代码如下:
template <class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){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()
{SmartPtr<int> p1(new int);//申请空间,因为有析构函数出了作用域自动释放SmartPtr<int> p2(new int);cout << div() << endl;
}int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}
2.2 智能指针的原理
上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:模板类中还得需要将* 、->重载下,才可让其像指针一样去使用。
template <class T>
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};
使用案例:
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};
struct Date //为了方便演示定义一个日期类
{int year;int month;int day;
};
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void Func()
{SmartPtr<int> p1(new int);*p1 = 10;cout << *p1 << endl;SmartPtr<Date> p2(new Date);//这里本来应该是p2.operator->()->year=2023;//语法上可以省略括号:p2->->year=2023;//为了语法可读性,可以写成p2->year=2023;p2->year = 2023;p2->month = 7;p2->day = 4;cout << p2->year << " " << p2->month << " " << p2->day << " " << endl;cout << div() << endl;
}int main()
{try{Func();}catch (exception& e){cout << e.what() << endl;}return 0;
}
2.3 小总结智能指针原理
RAII特性
重载operator*和opertaor->,具有像指针一样的行为。
3 智能指针的拷贝问题
在智能指针的基础上,来看一下下面这段代码:
class SmartPtr
{
public:SmartPtr(T* ptr):_ptr(ptr){}~SmartPtr(){delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};
int main()
{SmartPtr<int> sp1(new int(1));SmartPtr<int> sp2(sp1);return 0;
}
运行该代码,程序会直接崩溃,原因是同一块空间,析构了两次。
原因是这里的默认的拷贝构造函数是浅拷贝,但是这里不能用深拷贝来解决问题,因为C++原本的指针赋值就是浅拷贝。
C++提供以下这些库来解决浅拷贝问题
3.1 std::auto_ptr
C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。
auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原理
std::auto_ptr官方文档
// C++98 管理权转移 auto_ptrtemplate<class T>class auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){// 管理权转移sp._ptr = nullptr;//把被拷贝的指针置空了}auto_ptr<T>& operator=(auto_ptr<T>& ap){// 检测是否为自己给自己赋值if (this != &ap){// 释放当前对象中资源if (_ptr)delete _ptr;// 转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = NULL;}return *this;}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};// 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
int main()
{std::auto_ptr<int> sp1(new int);std::auto_ptr<int> sp2(sp1); // 管理权转移
// sp1被置空*sp2 = 10;cout << *sp2 << endl;cout << *sp1 << endl;return 0;
//}
主要的思想就是为了防止浅拷贝带来的重复析构同一块空间问题,auto_ptr这个库在处理的时候,拷贝完成后,就把被拷贝的指针给置空了,这样就不会出现重复析构了问题了。
但是这种方式显然是不合理的。
接下来重点介绍以下三个库
3.2 std::unique_ptr
C++11中开始提供更靠谱的unique_ptr
std::unique_ptr官方文档
C++11库才更新智能指针实现
C++11出来之前,boost搞出了更好用scoped_ptr/shared_ptr/weak_ptr
C++11将boost库中智能指针精华部分吸收了过来C++11->unique_ptr/shared_ptr/weak_ptr
unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理
template<class T>class unique_ptr{
public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}//因为类里面会默认写出拷贝构造函数和赋值函数,//所以这里要手动声明一下//后面加个 =delete 就是防止被拷贝的意思unique_ptr(const unique_ptr<T>&sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>&sp) = delete;
private:T* _ptr;
};
这里直接用库里的测试一下
确实是不让拷贝
3.3 std::shared_ptr
C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
std::shared_ptr官方文档
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
由于要使用到引用计数,如果有多个对象指向同一块空间,这里我们叫做空间a,需要统计有几个对象。
这里采用的方案是再开辟一块空间给引用计数,这块空间我们叫做空间b,当空间a被一个对象指向时,空间b就计数++,当一个对象不再指向该空间时,空间b就减1。当减到0时,说明这是最后一个对象指向空间a了,可以析构了。这样就避免了重复析构的问题。
如下图演示:
当有新增对象指向_ptr时,_count++;
当sp2不再指向_ptr时,_count-=1;
当sp1不再指向_ptr时,_count-=1,此时_count==0,可以析构了。
3.3.1 拷贝构造函数
这里拷贝构造函数代码分析如上图所示
template<class T>class my_shared_ptr{public:my_shared_ptr(T* ptr):_ptr(ptr),_count(new int(1)){}my_shared_ptr(const my_shared_ptr<T>& sp):_ptr(sp._ptr),_count(sp._count){add();//(*_count)++}void release()//--(*_count)并判断是否等于0{if (--(*_count) == 0){delete _ptr;delete _count;}}void add(){(*_count)++;}~my_shared_ptr(){release();//--(*_count)==0,才能析构}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;int* _count;//开辟一块引用计数的空间};
}
3.3.2 赋值函数
假如现在sp1,sp2,sp3指向了同一块空间,sp4指向另一空间。
现在要求赋值
sp1=sp4或者sp4=sp1
分析如下:
sp1=sp4
就是说sp1指向了sp4的空间。那么原来sp1指向的_count就要减去1
然后把sp1指向sp4即可,同时右边的_count要++
sp4=sp1
就是sp4指向的空间改为sp1指向的空间,然后sp4的_count-=1,由于_count==0,所以要析构掉图片右边的两个空间。左边_count++
代码如下:
my_shared_ptr<T>& operator=(my_shared_ptr<T>& sp){if (_ptr != sp._ptr){release();_ptr = sp._ptr;_count = sp._count;add();}return *this;}void release(){if (--(*_count) == 0){delete _ptr;delete _count;}}void add(){(*_count)++;}
3.4 std::shared_ptr的线程安全问题
接下来测试shared_ptr的线程安全问题。需要注意的是shared_ptr的线程安全分为两方面:
- 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时
++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2.这样引用计数就错
乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁
的,也就是说引用计数的操作是线程安全的。- 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。
3.4 .1智能指针引用计数操作加锁
namespace ffg
{template<class T>class my_shared_ptr{public:my_shared_ptr(T* ptr):_ptr(ptr),_count(new int(1)),_mtx(new mutex){}my_shared_ptr(const my_shared_ptr<T>& sp):_ptr(sp._ptr),_count(sp._count),_mtx(sp._mtx){add();}my_shared_ptr<T>& operator=(my_shared_ptr<T>& sp){if (_ptr != sp._ptr){release();_ptr = sp._ptr;_count = sp._count;_mtx = sp._mtx;add();}return *this;}void release()//凡是涉及到++,--的操作都需要加锁处理。注意这里锁的内存空间释放问题{//不能提前delete _mtx,因为_count可能还大于0_mtx->lock();bool flag = false;if (--(*_count) == 0){delete _ptr;delete _count;flag = true;}_mtx->unlock();if (flag){delete _mtx;}}void add()//加锁{_mtx->lock();(*_count)++;_mtx->unlock();}~my_shared_ptr(){release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}int use_count(){return *_count;}private:T* _ptr;int* _count;mutex* _mtx;};
}
测试代码:
3.4.2 外界内存资源是非线程安全的
外界资源未加锁前:
struct Date
{int _year = 0;int _month = 0;int _day = 0;
};
void SharePtrFunc(ffg::my_shared_ptr<Date>& sp, size_t n, mutex& mtx)
{cout << sp.get() << endl;for (size_t i = 0; i < n; ++i){// 这里智能指针拷贝会++计数,智能指针析构会--计数,加锁了,这里是线程安全的。//但是管理的资源并不是线程安全的,外界需要自己加锁处理,所以不是线程安全的。ffg::my_shared_ptr<Date> copy(sp);//mtx.lock();sp->_year++;sp->_month++;sp->_day++;//mtx.unlock();}
}
int main()
{ffg::my_shared_ptr<Date> p(new Date);cout << p.get() << endl;const size_t n = 10000;mutex mtx;thread t1(SharePtrFunc, std::ref(p), n, std::ref(mtx));thread t2(SharePtrFunc, std::ref(p), n, std::ref(mtx));t1.join();t2.join();cout << p->_year << endl;cout << p->_month << endl;cout << p->_day << endl;cout << p.use_count() << endl;return 0;
}
这里的输出应该p->_year,p->_month,p->_day都应该是20000,但是由于线程安全问题,可能会导致结果出现错误。
所以,需要程序员在外界加上锁。
void SharePtrFunc(ffg::my_shared_ptr<Date>& sp, size_t n, mutex& mtx)
{cout << sp.get() << endl;for (size_t i = 0; i < n; ++i){// 这里智能指针拷贝会++计数,智能指针析构会--计数,加锁了,这里是线程安全的。//但是管理的资源并不是线程安全的,外界需要自己加锁处理,所以不是线程安全的。ffg::my_shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_month++;sp->_day++;mtx.unlock();}
}
int main()
{ffg::my_shared_ptr<Date> p(new Date);cout << p.get() << endl;const size_t n = 10000;mutex mtx;thread t1(SharePtrFunc, std::ref(p), n, std::ref(mtx));thread t2(SharePtrFunc, std::ref(p), n, std::ref(mtx));t1.join();t2.join();cout << p->_year << endl;cout << p->_month << endl;cout << p->_day << endl;cout << p.use_count() << endl;return 0;
}
多次执行:
直接使用C++库的shared_ptr也同理。
3.5 std::shared_ptr 引发的循环引用问题
先来看以下这两段代码:
建立了两个链表节点,n1,n2相互连接
struct ListNode
{ffg::my_shared_ptr<ListNode> _pre;ffg::my_shared_ptr<ListNode> _next;~ListNode(){cout << "~ListNode" << endl;}
};int main()
{ffg::my_shared_ptr<ListNode> n1(new ListNode);ffg::my_shared_ptr<ListNode> n2(new ListNode);n1->_next = n2;//这里注释掉任意一行都能成功析构//n2->_pre = n1;return 0;
}
上面这段代码,在main函数里的
当有n1->_next=n2,或者n2->_pre=n1,不同时存在的时候n1,n2就能成功析构。
但是当这两行代码同时存在的时候,就不能成功析构。
先来分析以下第一种情况
这里假设是
n1->_next = n2;
如下图,当n2和左边的_next 指向右边的ListNode空间时,引用计数为2
然后n2析构了以后,右边的引用计数为1。
接下来到n1析构,引用计数为0,左边的_next随着ListNode的析构也被释放掉了,所以右边的引用计数也变为0,右边的ListNode也被析构了,_prve也不存在了。
接下来分析第二种情况,也就是循环引用的问题
n1->_next = n2;
n2->_pre = n1;
首先是n2先析构,右边的引用计数变为1
接下来是n1析构,左边的引用计数变为1
此时循环引用问题就来了。
n2和n1析构,引用计数都减到1,但是_next还是指向下一个节点,_prve还是指向上一个节点。
也就是说_next析构了,右边的ListNode就能释放,右边的_prev也能释放。
_prev析构了,左边的ListNode就能释放,左边的_next也能释放掉。
所以这两个ListNode互相牵制了,谁也不会释放。
左边的释放条件是右边释放,右边释放条件是左边释放。相互矛盾了。
这就是循环引用问题。
4 std::weak_ptr 解决循环引用问题
注意:
1.weak_ptr是专门设计出来,解决shard_ptr的循环引用问题。
2.它不是常规的智能指针,不支持RAII,不支持资源管理
3.支持像指针一样的功能
这里先使用库里面的weak_ptr看看效果
成功解决了循环引用问题。
4.1 weak_ptr解决循环引用的原理
原理很简单,就是:n1->_next = n2;和n2->_prev = n1;时weak_ptr的_next和_prev不会增加node1和node2的引用计数。
4.2 简易版weak_ptr的实现
class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const my_shared_ptr<T>& sp):_ptr(sp.get()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}private:T* _ptr;};
}
测试:
struct ListNode
{ffg::weak_ptr<ListNode> _pre;ffg::weak_ptr<ListNode> _next;~ListNode(){cout << "~ListNode" << endl;}
};int main()
{ffg::my_shared_ptr<ListNode> n1(new ListNode);ffg::my_shared_ptr<ListNode> n2(new ListNode);cout << n1.use_count() << endl;//1cout << n2.use_count() << endl;//1n1->_next = n2;n2->_pre = n1;return 0;
}
从图中可以看出引用计数变为1了,可以说明,_next和_prev不会在增加引用计数,这样就解决了循环引用问题。
5 删除器
如果不是new出来的对象如何通过智能指针管理呢?其实shared_ptr设计了一个删除器来解决这个问题。
库里面的删除器测试:
6 C++11和boost中智能指针的关系
- C++ 98 中产生了第一个智能指针auto_ptr.
- C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
- C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
- C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost 的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。
7 总结
智能指针一开始是为了解决某些情况下因抛异常而导致内存泄漏问题。
由RAII思想设计的智能指针,会管理资源的内存和释放。它支持像指针一样的功能
但是从而由智能指针引发了诸如拷贝问题,线程安全问题,循环引用问题。
智能指针是线程安全的,因为库里设计的时候加锁处理了,但是外界的资源不是线程安全的,需要程序员处理。
一开始的C++98库中的auto_ptr解决拷贝问题是不合理的,于是有了unique_ptr(强行让智能指针不能做拷贝),shared_ptr(引用计数解决拷贝问题中的重复析构),weak_ptr是配合shared_ptr使用了,解决了循环引用问题。
相关文章:

【C++学习笔记】RAII思想——智能指针
智能指针 1 内存泄漏问题2 RAII(Resource Acquisition Is Initialization)2.1 使用RAII思想设计的SmartPtr类2.2 智能指针的原理2.3 小总结智能指针原理 3 智能指针的拷贝问题3.1 std::auto_ptr3.2 std::unique_ptr3.3 std::shared_ptr3.3.1 拷贝构造函数…...

ubantu配置python环境
安装python 参考博客 安装pycharm 博客 创建Pycharm快捷方式 博客 ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1, currently the ‘ssl’ module is compiled with File “/home/r00t/IdeaProjects/data/venv/lib/python3.9/site-packages/urllib3/init.py”…...

单向/双向V2G环境下分布式电源与电动汽车充电站联合配置方法(matlab代码)
目录 1 主要内容 目标函数 电动汽车负荷建模 算例系统图 程序亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现博士文章《互动环境下分布式电源与电动汽车充电站的优化配置方法研究》第五章《单向/双向V2G环境下分布式电源与电动汽车充电站联合配置方法》…...

dockerfile常用指令
Dockerfile常用指令 视频学习资料来源这里,点击本行文字即可跳转,讲的比较详细,不过比较老,跟最新的肯定是有一些差异的 Dockerfile官网文档的话点击这里 中文文档可以看看这个,不过没有详细的代码demo 或者是看这个 或…...

Matlab/simulink与dsp28335联合开发教程
一.入门篇(开发环境搭建) 1.1 Code Composer Studio 软件安装1.2 MATLAB 软件安装1.3 Control_SUIT3.4 软件安装1.4 C2000 Simulink 开发工具箱安装1.5 Visual_Studio_Professional 二. 基础篇(片内外设使用) 2.1 G…...

新项目搞完啦!!!
大家好,我是鱼皮。 经过了 7 场直播,总时长近 20 小时,我在 自己的编程导航 的第 5 个 全程直播开发 的项目 —— 智能 BI 项目,完结啦! 我在这里对该项目做一个简单的总结,希望让更多需要它的同学看到&am…...

分享一个可交互的小场景(二)
先看效果: 可互动的小场景 再看代码: JS部分 <script>var rotateDiv document.getElementById(rot);var rotateIcons document.getElementById(rot-icons);var clickRotateDiv document.getElementById(click-rot);var angle 0;clickRotateDi…...

2.5 DNS 应用 -- 1. DNS 概述
2.5 DNS 应用 -- 1. DNS 概述 DNS:Domain Name SystemDNS分布式层次式数据库DNS根域名服务器TLD和权威域名解析服务器本地域名解析服务器 DNS 查询迭代查询递归查询 DNS记录缓存和更新 DNS:Domain Name System Internet上主机/路由器的识别问题 IP地址域…...

基于STM32麦克风阵列音频信号处理系统设计
v hezkz17进数字音频系统研究开发交流答疑 附录: ADAU1452音频处理系统...

《重构》:Extract Class and Inline Class
hey,gays lets go on to refator those sh!t . i fork a rep, this the link GitHub - TIMPICKLE/refator-code: 重构 - 改善既有代码的设计 all right, lets see the genel description. 提取类 对立:内联类 目的:将大类分成小类 场景&a…...

腾讯云对象存储联合DataBend云数仓打通数据湖和数据仓库
随着数字化进程不断深入,数据呈大规模、多样性的爆发式增长。为满足更多样、更复杂的业务数据处理分析的诉求,湖仓一体应运而生。在Gartner发布的《Hype Cycle for Data Management 2021》中,湖仓一体(Lake house)首次…...

ExceptionLess windows部署。
前言 windows部署 1、一个api项目,里面包含了所有api。 2、一个elasticsearch项目,用来存储相关数据。 3、一个UI项目,也就是查看异常、设置新异常邮件通知等操作的后台。 异常在客户端提交的代码原理:一个异常被提交,…...

使用python实现1DCNN-GRU回归预测
要实现1DCNN-GRU进行回归预测,您可以使用以下Python代码作为参考: 首先,导入所需的库: import numpy as np import tensorflow as tf from tensorflow.keras.layers import Conv1D, MaxPooling1D, GlobalAveragePooling1D, GRU,…...

移动端数据可视化设计
在做APP设计的时候,难免会遇到一些需要展示数据的场景。使用传统的表格和文档展示数据不仅难看,也影响用户理解数据的含义。而数据可视化设计能将数据以更加直观的方式展现出来,使数据更加客观、更有说服力。 在移动应用中,数据可…...

Linux文件系统概述
本文已收录至《Linux知识与编程》专栏! 作者:ARMCSKGT 演示环境:CentOS 7 文件系统概述 前言正文文件与磁盘磁盘介绍与机械硬盘机械硬盘基础结构机械硬盘数据存储与管理 文件操作的细节创建文件访问文件删除文件恢复文件其他情况 最后 前言 …...

go专业数据结构与算法
go语言之专业数据结构与算法 2.数组概念 3.golang实现数组结构 4.golang实现数组迭代器 5.数组栈的高级实现 6.栈模拟低级递归 7.斐波那契数列栈模拟递归 8.递归实现文件夹遍历 9.栈模拟文件递归 10.层级展示文件夹 11.数组队列的实现 12.队列实现遍历文件夹 13.循环队列 14.链…...

Hive on Spark的小文件设置参数
Hive on Spark的小文件设置参数 参数调优 了解完了Spark作业运行的基本原理之后,对资源相关的参数就容易理解了。所谓的Spark资源参数调优,其实主要就是对Spark运行过程中各个使用资源的地方,通过调节各种参数,来优化资源使用的效…...

高级SQL语句
目录 MySQL 高级(进阶) SQL 语句函数数学函数:聚合函数字符串函数: 连接查询inner join(内连接):left join(左连接):right join(右连接): CREATE VIEW(视图)UNION(联集)C…...

IDE /skipping incompatible xxx_d.dll when searching for -lxxx_d
文章目录 概述场景复现用以测试的代码编译器位数不匹配导致?保持编译器类型一致再验证编译器位数的影响MingW下调用OS的库咋不告警?以mingW下使用winSocket为例MingW下网络编程的头文件分析该环境下链接的ws2_32库文件在哪里?mingW为啥可以兼容window下的动态库 概…...

C语言学习准备-编辑器选择
今天继续给大家更新C语言经典案例 今天的案例会比昨天稍微有一些难度,但是同时还是非常经典的案例 本来是想给大家继续更新C语言经典案例,但是有朋友反应C语言编辑器的选择,刚好我自己也是想更换一下C语言的编辑器,跟大家分享一下…...

微信为什么使用 SQLite 保存聊天记录?
概要 SQLite 是一个被大家低估的数据库,但有些人认为它是一个不适合生产环境使用的玩具数据库。事实上,SQLite 是一个非常可靠的数据库,它可以处理 TB 级的数据,但它没有网络层。接下来,本文将与大家共同探讨 SQLite 在…...

VB串口通讯方式解释
目前,Visual Basic (简称VB) 已成为WINDOWS 系统开发的主要语言,以其高效、简单易学及功能强大的特点越来越为广大程序设计人员及用户所青睐。VB 支持面向对象的程序设计,具有结构化的事件驱动编程模式并可以使用无限扩增的控件。在VB 应用程序中可以方便地调用WINDOWS API函数…...

Mybatis-Plus不能更新对象字段为空值问题解决
问题描述: 在使用Mybatis-Plus调用updateById方法进行数据更新默认情况下是不能更新空值字段的,而在实际开发过程中,往往会遇到需要将字段值更新为空值的情况,该如何解决呢? 原因分析: Mybatis-Plus中字…...

d3dx9_43.dll丢失怎么解决
d3dx9_43.dll丢失的影响 当我们在运行某些需要DirectX 9支持的程序时,如果系统中缺少d3dx9_43.dll文件,就会出现错误提示,导致程序无法正常启动。这个错误提示通常会类似于“找不到d3dx9_43.dll”或“d3dx9_43.dll不存在”。 打开电脑浏览器…...

【花雕】全国青少年机器人技术一级考试备考实操搭建手册8
随着科技的不断进步,机器人技术已经成为了一个重要的领域。在这个领域中,机械结构是机器人设计中至关重要的一部分,它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说,了解机械结构的基础知识,掌…...

【UE5 Cesium】09-Cesium for Unreal 子关卡应用实例(下)
效果 通过按钮点击事件实现子关卡的切换 步骤 新建两个Actor蓝图作为GeoMarker,分别命名为“BP_GeoMarker_BeiJing”、“BP_GeoMarker_ShangHai” 分别打开这两个蓝图,添加文本渲染组件 在指定的地理位置上拖入蓝图“BP_GeoMarker_BeiJing” 控制“BP_…...

插值应用案例2
案例1 高点和高程 在一丘陵地带测量高程,x和y方向每隔100m测一个点,得到高程如下表所列,试插值一曲面,确定合适的模型,并由此测到最高点和相应的高程。 x0/z0\y0 100 200 300 400 500 100 636 697 624 478 …...

【新星计划Linux】——常用命令(1)
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.常用命令 1.Linux的基本原则: 用户接口: 2.命令形…...

python应用-excel和数据库的读取及写入操作
近日完成一个交办任务,从excel表读取数据,根据ID在数据库表匹配相应的记录,并回填至excel表里。我使用的工具是python。下面记录下相应的模块。 一、从excel表读取数据 import pandas as pd import numpy as npdef read_excel():path &quo…...

MySQL Optimization Learning(一)
目录 一、MySQL性能监控 1、show profile 2、performance schema 2.1、MYSQL performance schema详解 3、show processlist 一、MySQL性能监控 MySQL官网 拖到首页最下方找到 MySQL Reference Manual ->cmd命令行 C:\Users\Administrator>mysql -uroot -proot …...