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

C++ STL vector 模拟实现

✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:C++之STL
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:上次我们已经数字会用了vector,这次我们对其底层更深一步挖掘,其中重点是,Vector中一些深浅拷贝问题。

目录

一.Vector模拟实现的整体框架

二. Vector的构造与析构

三.size(),capacity()

 四.reserve(),resize()

1.reserve()

2.resize

五.push_back(),pop_back()

1.push_back()

2. pop_back()

六.Vector的迭代器

 七.operator [ ]

 八.insert(),erase()

1.迭代器失效

2.insert()

3.erase()

九.再看Vector构造函数

十.拷贝构造

1.深浅拷贝

2.正确的拷贝构造代码:

3.正确的 reserve()

4.赋值运算符重载

十一.总体代码


一.Vector模拟实现的整体框架

我们先认识一下Vector的整体模拟实现框架,Vector在功能上就是我们数据结构阶段实现的顺序表基本一致,但是Vector在成员框架上与顺序表有所不同,且Vector使用类和对象封装支持模板泛型。

template<class T>
class Vector
{
public://迭代器类型typedef T* iterator;typedef const T* const_iterator;//...private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址
};

Vector的迭代器是对顺序表原生类型指针的封装。

二. Vector的构造与析构

Vector主要是对成员变量初始化:

	Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}

析构主要释放我们申请的空间:

    ~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}

三.size(),capacity()

size()返回当前顺序表存储的数据个数,capacity()返回当前顺序表的容量。

    size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}

 四.reserve(),resize()

1.reserve()

设置Vector的容量,注意容量支持增加,但是不支持减小。

void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){memcpy(tmp, _start, sizeof(T) * capacity()); /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}

注意:

此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,然后计算的size也并非是,准确的size。除此之外这份代码依旧是有问题的。我们后面解释。

错误代码如下:

    void reserve(size_t capa){if (capacity() < capa){iterator tmp = new T[capa];if (_start){memcpy(tmp, _start, sizeof(T) * capacity());delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + size();_end_of_storage = _start + capa;}}

2.resize

提供改变存储数据的个数的能力。如果 n < size 时就是删除数据,n > size且空间不够时需要扩容+初始化,空间足够,仅需要初始化剩下的空间。

    void resize(size_t n, T val = T()){	//1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_finish++;}}}

五.push_back(),pop_back()

1.push_back()

从尾部插入一个数据。

	void push_back(const T& val){//检查是否需要扩容if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//插入数据*(_finish) = val;_finish++;}

2. pop_back()

	bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}

六.Vector的迭代器

	typedef T* iterator;typedef const T* const_iterator;

Vector底层就是顺序存储的结构,所以可以使用原生指针作为迭代器。

	//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}

有了迭代器就可以支持迭代器访问,和范围for。

int main()
{Vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700);Vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v1){cout << e << " ";}return 0;
}

 七.operator [ ]

	//穿引用返回T& operator[](size_t pos){//判断位置的合法性assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

 八.insert(),erase()

1.迭代器失效

在模拟实现之前我们先看一下什么是迭代器失效问题:

迭代器失效问题通常发生在容器类的成员函数中,例如erase和insert。在这些函数中,迭代器被重置或修改,导致原始迭代器不再指向容器的正确位置,从而导致迭代器失效。

int main()
{vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700);  vector<int>::iterator pos = find(v1.begin(), v1.end(),200);//对pos位置插入v1.insert(pos, 150);//pos已经失效v1.insert(pos, 170);return 0;
}

原理图:

情况一:

 上述代码中的迭代器失效问题也是属于这种情况。

情况二:

2.insert()

    iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}

在使用pos时要注意扩容会使得pos失效,需要重新计算pos位置。

3.erase()

	iterator erase(iterator pos){//判断位置是否合法assert(pos >= _start);assert(pos < _finish);iterator end = pos ;/挪动数据删除while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}

九.再看Vector构造函数

std中的vector还支持使用指定个数和初始化值初始化,和迭代器区间初始化。这两个功能在我们平时也是能用到的。

    //1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用错误Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}

第三个构造函数的作用是防止构造调用错误冲突,在我们进行如下的调用时:

Vector<int> v2(5, 10);

编译器会以为我们在调用迭代器区间初始化构造函数,因为经过模板的推导,只有迭代器区间初始化构造函数,更适合这个调用。然后将一个整形当作地址在迭代器区间初始化构造函数里面解引用了,报错是:非法的间接寻址

 正常调用结果:

十.拷贝构造

今天这里编译器默认生成的拷贝构造显然是不能用了。

1.深浅拷贝

万万不可以直接使用拷贝函数按二进制或者按字节直接拷贝了。

错误代码1:

	Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//万万不可以直接按二进制拷贝memcpy(_start, v._start, sizeof(T) * v.capacity()); /*error!!!!*/_finish = _start + v.size();_end_of_storage = _start + v.capacity();}

原因:

调用处代码:


int main()
{string str("abcdefg");Vector<string> v2(5,str);Vector<string> v3(v2);return 0;
}

 会使得我们同一块空间被delete两次从而引发内存错误。

2.正确的拷贝构造代码:

	Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,//会使用string的赋值构造,完成深拷贝。for (int i = 0; i < v.size(); i++){push_back(v[i]);}}//现代写法Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}

错误代码2:reserve()

	void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.memcpy(tmp, _start, sizeof(T) * capacity());   /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}

这里我们仍然是使用了memcpy。

调用处代码:

int main()
{string str("abcdefg");Vector<string> v2;for (int i = 0; i < 6; i++){v2.push_back(str);}return 0;
}

3.正确的 reserve()

void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity());   /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}

这里有一个细节就是在reserve和拷贝构造的拷贝数据的时候我们都是使用了赋值。问题我们并没有重载赋值运算符,编译器自动生成,简单来说就是这里又会是一个浅拷贝。

4.赋值运算符重载

    //传统写法Vector<T>& operator=(const Vector<T>& v){T* tmp = new T[v.capacity()];if (_start){for (int i = 0; i < v.size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + v.size();_end_of_storage = _start + v.capacity();return *this;}//现代写法void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}Vector<T>& operator=(Vector<T> v){swap(v);return *this;}

现代写法利用,拷贝构造拷贝出来的对象,然后交换对象的成员。

十一.总体代码

#pragma once
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cassert>
using namespace std;template<class T>
class Vector
{
public:typedef T* iterator;typedef const T* const_iterator;Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}//1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用冲突Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//传统写法 拷贝构造//Vector(const Vector<T>& v)//	:_start(nullptr),//	_finish(nullptr),//	_end_of_storage(nullptr)//{//	reserve(v.capacity());//	//这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,//	//会使用string的赋值构造,完成深拷贝。//	for (int i = 0; i < v.size(); i++)//	{//		_start[i] = v[i];//	}//}//现代写法,拷贝构造Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}//传统写法,赋值拷贝//Vector<T>& operator=(const Vector<T>& v)//{//	T* tmp = new T[v.capacity()];//	if (_start)//	{//		for (int i = 0; i < v.size(); i++)//		{//			tmp[i] = _start[i];//		}//		delete[] _start;//	}//	_start = tmp;//	_finish = _start + v.size();//	_end_of_storage = _start + v.capacity();//	//	return *this;//}void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}//现代写法,赋值拷贝Vector<T>& operator=(Vector<T> v){swap(v);return *this;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity());   /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}void resize(size_t n, T val = T()){	//1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_finish++;}}}//穿引用返回T& operator[](size_t pos){//判断位置的合法性assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}void push_back(const T& val){if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//内置类型直接赋值,自定义类型使用赋值构造*(_finish) = val;_finish++;}bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator end = pos ;while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址int tmp;
};

 

相关文章:

C++ STL vector 模拟实现

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;C之STL &#x1f525;<3>创作者&#xff1a;我的代码爱吃辣 ☂️<4>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<5>前言&#xff1a;上次我们已经数字会用…...

51单片机学习--红外遥控(外部中断)

需要利用下面这个红外接收头&#xff0c;OUT口会发出红外信号对应的高低电平&#xff0c;由于发送的速度很快&#xff0c;所以需要把OUT引脚接在外部中断引脚上&#xff0c;当OUT一旦产生下降沿&#xff0c;马上进中断&#xff0c;这样响应会更及时。 外部中断引脚位于P3_2和P…...

后端开发10.规格模块

概述 简介 效果图...

腾讯出了一个新聊天软件M8

众所周知&#xff0c;如今国内互联网&#xff0c;微信和QQ无疑是社交领域的霸主。 下载:https://www.123pan.com/s/BP5A-RW4xh.html 不过&#xff0c;它们也有各自局限性&#xff0c;比如难以结识新朋友、功能过于复杂等。 这让用户产生厌倦&#xff0c;再加上近几年AI、元宇…...

C++ QT(一)

目录 初识QtQt 是什么Qt 能做什么Qt/C与QML 如何选择Qt 版本Windows 下安装QtLinux 下安装Qt安装Qt配置Qt Creator 输入中文配置Ubuntu 中文环境配置中文输入法 Qt Creator 简单使用Qt Creator 界面组成Qt Creator 设置 第一个Qt 程序新建一个项目项目文件介绍项目文件*.pro样式…...

微信小程序时钟

微信小程序自定义时钟&#xff0c;模拟翻牌时钟。1、页面布局 <view class"date-time-box"><view class"date-box">{{nowDate}}</view><view class"time-box"><view><image class"pic01 {{move[0]?move…...

HttpRunner自动化工具之设置代理和请求证书验证

httprunner设置代理&#xff1a; httprunner 库本身没有提供设置代理的接口&#xff0c;但是底层使用了urllib.requests 等库&#xff0c;可以设置HTTP_PROXY 和HTTPS_PROXY 环境变量&#xff0c;常用的网络库会自动识别这些环境变量。 日常调试使用代理&#xff08;如charles…...

opsForHash() 与 opsForValue 请问有什么区别?

&#x1f449;&#xff1a;&#x1f517;官方API参考手册 如图&#xff0c;opsForHash()返回HashOperations<K,HK,HV>但是 opsForValue()返回ValueOperations<K,V>… 区别就是opsForHash的返回值泛型中有K,HK,HV,其中K是Redis指定的某个数据库里面某一个关键字(由…...

具有吸引子的非线性系统(MatlabSimulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Linux一些常见的命令

1. 基础命令 1. ls&#xff1a; 列出目录内容。- 例如&#xff1a;ls -l 以长格式列出文件和目录。2. cd&#xff1a; 切换工作目录。- 例如&#xff1a;cd /home/user 进入 /home/user 目录。3. pwd&#xff1a; 显示当前工作目录的路径。4. mkdir&#xff1a; 创建新目录。-…...

正则表达式的基本知识

正则表达式是一种用于匹配和操作字符串的强大工具。它是由一系列字符和特殊符号组成的模式&#xff0c;可以用来检查字符串是否符合某种模式&#xff0c;进行匹配、替换、提取等操作。 下面是一些常见的正则表达式元字符和语法&#xff1a; 1. 字符匹配&#xff1a; - 普通…...

如何⽤webpack 来优化前端性能

如何⽤webpack 来优化前端性能&#xff1f; ⽤webpack 优化前端性能是指优化 webpack 的输出结果&#xff0c;让打包的最终结果在浏览器运⾏快速⾼效。 压缩代码&#xff1a;删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUgl…...

人机交互中的混合多重反馈

人机交互中态、势、感、知的混合多重反馈是指在交互过程中综合运用不同方面的反馈信息&#xff0c;包括用户态度&#xff08;态&#xff09;、行为动势&#xff08;势&#xff09;、情感体验&#xff08;感&#xff09;和认知反馈&#xff08;知&#xff09;。这种多重反馈可以…...

CSS:服务器字体 与 响应式布局(用法 + 例子 + 效果)

文章目录 服务器字体定义 服务器字体使用例子 响应式布局设备类型设备特性例子 服务器字体 解决字体不一致而产生的。 首先&#xff0c;在网上把字体下载好。 定义 服务器字体 font-face{font-family:字体名称;src:url(字体资源路径); }使用 在需要使用的选择器里加上 font…...

24届近3年上海电力大学自动化考研院校分析

今天给大家带来的是上海电力大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、上海电力大学 学校简介 上海电力大学&#xff08;Shanghai University of Electric Power&#xff09;&#xff0c;位于上海市&#xff0c;是中央与上海市共建、以上海市管理为主的全日…...

PostgreSQL查询慢sql原因和优化方案

PostgreSQL sql查询慢优化方案有一下几种解决方案&#xff1a; 1.关闭会话 查询慢sql的执行会话&#xff0c;关闭进程。 查看数据库后台连接进程 SELECT count(*) FROM pg_stat_activity;SELECT * FROM pg_stat_activity; 查看数据库后台连接进程&#xff0c;但是此条SQL不…...

Leetcode 21. 合并两个有序链表

题目描述 题目链接&#xff1a;https://leetcode.cn/problems/merge-two-sorted-lists/description/ 思路 两个链表都是升序链表&#xff0c;新建一个链表&#xff0c;引入伪头节点作为辅助节点&#xff0c;将各节点添加到伪节点之后&#xff0c;再用一个cur节点指向新链表的…...

[tool] Ubuntu 设置开机启动python脚本

前言&#xff1a; 话说我每次设置的服务器&#xff0c;再次开机&#xff0c;ip都会随之改变&#xff0c;固定ip有时候确定不好用。所以为啥不让让每次启动都发送ip给我呢。 步骤: sudo touch /etc/rc.local sudo chmod 777 /etc/rc.local sudo systemctl enable rc-local.se…...

「何」到底该读「なん」还是「なに」?柯桥学日语

「何」到底该读「なん」还是「なに」&#xff1f; 首先&#xff0c;讲一个规律&#xff0c;大家记住就行。当「何」后面所接单词的第一个发音在“た”、“だ”、“な”行时&#xff0c;读作“なん”。一般这种情况下&#xff0c;后面跟的是の、でも、です和だ。 用例&#xff…...

github - 创建组织-Team

地址记录&#xff1a; github创建组织(organization) - 知乎...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...