当前位置: 首页 > 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) - 知乎...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...