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

vector

目录

vector的成员函数:

at:

​编辑

 size:

assign:赋值

insert

find?

erase

swap

shrink_to_fit

​编辑

 vector的模拟实现:

vector的框架:

构造函数:

size和capacity

 reserve函数:

begin和end

[]

尾插函数:

 const修饰的begin和end

const修饰的[]

empty函数:

resize:

尾删

insert:

标准库里面的find函数:


vector的成员函数:

at:

 at表示访问vector的第n个位置的数据。

我们进行实验:

#include<stdio.h>
#include<vector>
#include<iostream>
using namespace std;void vector_test1()
{vector<int> v;v.resize(10, 5);for (int i = 0; i < v.size(); i++){cout << v.at(i) << " ";}cout << endl;
}
int main()
{vector_test1();return 0;
}

我们使用resize对v进行初始化,初始化的结果是vector有是个整型元素,十个元素都是5,我们遍历v,使用at函数,打印vector的每一个元素。

 size:

size函数的返回值是vector中有效元素的个数。

void func(const vector<int>&v)
{v.size();
}
void test_vector2()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.size();func(v);
}
int main()
{/*vector_test1();*/test_vector2();return 0;
}

如图所示的函数,为什么func这里需要传引用呢?

答:因为我们的vector中存储的数据类型不仅可能是内置类型,也有可能是自定义类型,传值传参的过程本身就是拷贝,对于内置类型,拷贝的消耗不大,但是对于自定义类型,拷贝的消耗就很大,所以我们用传引用传参提高效率。

如图所示,为什么传参时需要加上const呢?

答:因为我们是传引用传参,我们得到v是为了读取到v上的size(),假如我们不加const,我们对v或v上的数据进行修改时,v本身也会做出相应的变化,所以我们加上const,表示我们的v是只读的,防止函数内部对v进行修改。

 如图所示:这两个size函数的调用是同一个size吗?

 答:如图所示:

是同一个size,因为我们的size只有一个接口函数,加上const的原因是我们的size是vector的基础属性,我们可以读取size,但是不能对size进行修改。我们在传参时,发生了权限的缩小,我们的v在test_vector2函数中是可读可写的,但是到了func函数中,我们的v变成了只读函数。

 同样是vector函数,为什么size函数只有一个接口函数,而[]函数却有两个接口函数?

void func(const vector<int>&v)
{v[0];v[0]++;
}
void test_vector2()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v[0];v[0]++;func(v);
}
int main()
{/*vector_test1();*/test_vector2();return 0;
}

 我们进行编译:

 func的v[0]++出现错误:原因如下:

答:在test_vector2中,因为我们的v是可读可写的,所以我们的[]匹配的就是这个函数:

 在func2中,因为我们的v被const修饰,所以我们的v是只读的,我们调用[]函数匹配的就是这个函数:

 因为我们的[]被const修饰,所以我们只能读而不能修改。

总结:1:只读函数,const例如:size()

我们的size()函数本身就是只读的,所以我们用可读的对象或者可读可写的对象都可以调用size()函数。

2:只写函数,非const  例如:push_back()

 我们的push_back函数必须要求调用函数的对象必须是可写的,也就是说const修饰的对象不能调用push_back函数。

3:可读可写的函数  例如:[]和at()函数

 我们这些函数有两个接口函数,当我们调用函数的对象是可读可写的,我们就调用第一个函数,假如我们调用函数的对象是只读的,我们就调用第二个函数。

 问题:at()和[]函数的区别在哪里?

我们先写一串代码:

void test_vector3()
{vector<int> v;v.reserve(10);for (size_t i = 0; i < 10; ++i){v[i] = i;}
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

我们进行运行会报错:

这里报的错误是断言错误,原因如下:我们虽然reserve开了10个空间,但是我们并没有resize,所以我们的size依旧是0,我们的[]内部的实现里有一个断言函数:要求[i]中的i要小于size,我们的size为0,所以不满足断言,所以会报错。

 假如我们把这里的[]换成at呢?还会报错吗?

 依旧会报错,但是这里报错的形式是抛异常。

总结:at和[]的不同点在于报错的形式:

at是通过抛异常的形式进行报错。

[]是通过断言的形式进行报错。

断言报错是有缺点的,因为assert只有在Debug版本下才能生效,在release版本下会失效。

assign:赋值

 assign有两种写法,第一种写法是以迭代器区间的形式进行赋值,第二种就是普通的赋值。

问题1:为什么这里需要用类摸板,为什么不直接用迭代器。

答:原因是我们的vector中不仅会有内置类型,也可能会有自定义类型,对于自定义类型的迭代器,我们可以用类模板实现。

我们对两种函数进行实验:

void test_vector3()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.assign(10, 1);for (auto e : v){cout << e << " ";}cout << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

 assign表示把vector的前十个元素都赋值为1.

assign的第二种应用。

void test_vector3()
{vector<int> v,v1;v.push_back(1);v.push_back(2);v.push_back(3);v1.assign(v.begin(), v.end());for (auto e : v1){cout << e << " ";}cout << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

assign这里表示把从迭代器v.begin()到v.end()的全部元素都赋值给v1.

我们进行调用。

 接下来,我们尝试用string进行赋值。

void test_vector3()
{/*vector<int> v,v1;v.push_back(1);v.push_back(2);v.push_back(3);*/vector<int> v;string str("hello world");v.assign(str.begin(), str.end());//v1.assign(v.begin(), v.end());for (auto e : v){cout << e << " ";}cout << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

我们把迭代器str.begin()到str.end()的元素赋值给v,我们进行打印。

 之所以是数字:我们的vector内的数据类型是int,所以把string中的char类型强制转换成为了int,变成了数字。

insert

 我们的string的insert函数的参数使用的是下标,而我们的vecor的insert使用的都是迭代器。

我们在vector中不提供头插和头删,原因是头插和头删需要挪动数据,造成效率降低。

如果我们真的想要使用头插或者头删时,我们可以使用insert和erase来代替。

例如:

void test_vector3()
{vector<int> v;v.reserve(10);v.insert(v.begin(), 1);for (int i = 0; i < v.size(); i++){cout << v[i] << "";}cout << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

find?

 我们发现,vector的成员函数并没有find,为什么呢?

我们在std标准库找到了find函数

 因为除了string容器之外,其他容器的find函数都是通过迭代器实现的,所以我们把这些容器的find函数直接写成一个,直接写在标准库中。

 我们从迭代器first找到last,因为last是容器最后一个元素的下一个位置,所以last一定不是需要找的元素,所以当找到时,我们返回对应的迭代器,没有找到,我们返回last这个迭代器即可。

我们进行实验:

void test_vector3()
{vector<int> v;v.resize(10);for (size_t i = 0; i < v.size(); i++){v[i] = i;}vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto e : v){cout << e << " ";}cout << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

我们的目的是从v中找到3的位置,在3的位置插入10。

 问题:string为什么不使用标准库里面的find函数?

答:1:因为string出现的比较早,string出现的时候,标准库还没有完善。

2:因为string可能存在查找子串,我们用迭代器无法实现查找子串。

erase

表示删除vector的元素,分为两个接口函数,第一个接口函数是删除迭代器位置的一个元素,第二个接口函数是删除两个迭代器之间的元素。
具体的使用案例我们在网站就能查到,这里不再赘述。

swap

我们知道,算法库里面也有一个swap函数:

 这两个函数哪一个函数更建议使用?

答:我们更建议使用vector的成员函数swap,如图所示:

 标准库里面的swap函数是通过类模板实现的,并且中间使用了三次拷贝构造函数,因为vector中的数据类型也可能是自定义类型,对于自定义类型的拷贝构造效率太低,所以我们尽量使用vector自己提供的swap函数。

 既然vector已经提供了swap函数,但是为什么标准库std又特殊的为vector提供了swap函数,如图所示:

 我们写一串代码进行分析:

void test_vector3()
{vector<int> v,v1;v1.push_back(10);v1.push_back(20);v1.push_back(30);v1.swap(v);swap(v1, v);
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

这里出现了两个swap函数,第一个swap函数是vector的成员函数,第二个swap函数是标准库std里面的swap函数。

 我们知道,算法库里面的swap函数走的是三次深拷贝,但是我们这里的第二个函数调用的就是算法库里面的swap,是否真的会调用三次深拷贝呢?

答:并不会,如图所示:

 我们知道,编译器在匹配摸板的时候,一定找最合适的进行匹配,因为我们已经写好了我们是vector的摸板,所以调用算法库里面的swap函数直接匹配这里的摸板,有了摸板就不需要多次深拷贝了。

简单的说,如图所示:

 我们这里调用的swap(v1,v)会进行检查,编译器发现swap的两个参数都是vector,所以我们直接调用转换为调用vector的成员函数swap,也就是这样:v1.swap(v)

所以我们调用的这两个函数,最终的结果是一样的,第一个函数直接调用vector的成员函数swap,第二个调用标准库的swap函数,swap函数再进行识别,我们转而调用vector的成员函数swap。
 

shrink_to_fit

表示把vector的容量调整到和vector的size相同。

提出一个问题:缩容适合使用吗?

不适合,原因如下:首先,缩容是异地缩容,如图所示:

 我们的size是8,我们的capacity是16,假如我们要进行缩容,我们首先开辟一个空间大小为8的一份空间:

 然后我们把v1的有效元素放到tmp上,然后让v1指向tmp,然后释放掉v1即可。

这就是异地缩容的原理,所以异地缩容比较繁琐,效率低下,有没有原地缩容?

答:并没有原地缩容,例如:

 

 这是我们向内存申请的空间,假如我们想要释放空间的话,我们是不允许从middle位置释放空间的,因为这一部分空间是连贯的,我们只能从begin位置释放整个空间,所以也就不支持本地缩容。

vector函数支持缩容吗?

答:不支持,我们进行实验:

void test_vector3()
{vector<int> v,v1;v1.reserve(10);v1.push_back(10);v1.push_back(20);v1.push_back(30);cout << v1.capacity() << endl;v1.reserve(3);cout << v1.capacity() << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

 假如vector支持缩容时,我们打印的结果应该为3.

所以vector并不支持缩容。

 resize可以缩容吗?

答:不可以,我们进行实验:

void test_vector3()
{vector<int> v,v1;v1.reserve(10);v1.push_back(10);v1.push_back(20);v1.push_back(30);cout << v1.capacity() << endl;v1.resize(3);cout << v1.capacity() << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

我们进行运行:

resize也不能缩容。

总结:vector一般不支持缩容。 

 在如今的计算机领域,硬件存储空间越来越大的情况下,时间就要比空间重要的多,所以我们一般不频繁使用缩容函数,因为缩容一定是异地缩容,异地缩容的本质是以时间换空间。

我们对shrink_to_fit进行实验:

void test_vector3()
{vector<int> v,v1;v1.reserve(10);v1.push_back(10);v1.push_back(20);v1.push_back(30);cout << v1.capacity() << endl;v1.shrink_to_fit();cout << v1.capacity() << endl;
}
int main()
{/*vector_test1();*/test_vector3();return 0;
}

我们进行运行:

成功缩容。

 vector的模拟实现:

我们首先看一下vector的源代码,我们通过调试就可以看pj版本的源代码:

 vector在pj版本下的源代码大概是3000行,这里的内容是开源声明。

sgi的版本更适合我们初学者学习,我们查看sgi版本。

我们把我们在源码中找最重要的代码,进行精简:

 我们可以发现,vector的成员变量的雷西那个都是迭代器。

vector的结构图如图所示:

 我们按照这个框架模拟实现vector

vector的框架:

#pragma once
namespace bit
{template<class T>clasee vector{public:typedef T* iterator;private:iterator _start;iterator _finish;iterator _endofstorge;
};
}

T是我们定义的摸板,我们把T*类型重命名为iterator,我们有三个成员变量。

构造函数:

 这是源代码中vector的构造函数,其中使用了初始化列表,把三个迭代器都置为0,我们按照他的写法实现构造函数。

vector():_start(nullptr), _finish(nullptr), _endofstorge(nullptr){}

我们的三个成员变量都是迭代器,我们可以先把迭代器理解为指针,对于指针的初始化,我们可以赋值nullptr,初始化列表是在调用函数内容之前就已经实现的。

size和capacity

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

我们把迭代器先理解为指针,指针与指针相减的结果为数字,size是有效元素的个数,capacity是容量,我们根据图像就能够写出对应的函数。

 reserve函数:

void reserve(size_t n){if (n > capacity()){T*tmp = new T*[n];memcpy(tmp, _start, sizeof(T)*size());delete[] _start;_start = tmp;_finish = _start + size();_endofstorge = _start + n;}}

我们进行画图解释:

 只有当n大于我们的capacity时,我们才进行扩容,我们的reserve不缩容。

我们的思路是异地扩容,我们新申请一个大小为n的空间tmp,然后把vector上的数据拷贝到tmp上,然后释放掉原来vector的数据,让新的空间指向tmp,再对_finish和endofstorge进行修改。

这里可以用malloc初始化吗?

答:不能,因为malloc只会对内置类型初始化,对于自定义类型不初始化。

new相较于malloc的优点:

malloc需要判断是否申请空间失败,而new不需要,new失败的话会抛异常。 

begin和end

iterator begin(){return _start;}iterator end(){return _finish;}

[]

T&operator[](size_t pos){assert(pos < size());return _start[pos];}

我们的[]函数内部必须要断言:要求pos一定要小于size()

尾插函数:

void push_back(const T&x){if (_finish == _endofstorge){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}

我们进行尾插的数据类型 T既有可能是内置类型,也有可能是自定义类型,对于内置类型,我们可以不传引用,但是对于自定义类型必须传引用。

我们传了引用,在函数中可能会导致x的值发生变化,我们加上const表示x是只读的。

 

 假如我们的元素数和容量相等,表示我们需要扩容了,又因为capacity的初始化值可能为0,所以我们使用三目操作符,假如capacity为0时,我们把4赋给newCapacity,然后调用reserve函数进行扩容。

判断并执行扩容之后,接下来,进行尾插元素,我们的迭代器可以理解为指针 ,我们在_finish插入元素x,再++_finish即可。

特殊情况:

 当我们的vector没有一个元素,我们进行尾插时,我们的newcapacity就被设置为4,然后我们调用reserve函数进行扩容:

 这时候,因为我们vector中的元素实际为空,所以我们并不需要进行内存拷贝,所以我们可以优化一下reserve函数

void reserve(size_t n){if (n > capacity()){T*tmp = new T*[n];if (_start){memcpy(tmp, _start, sizeof(T)*size());delete[] _start;}_start = tmp;_finish = _start + size();_endofstorge = _start + n;}}

接下来,我们测试之前写的代码是否有误。

测试代码

using namespace std;
#include<iostream>
#include"vector.h"
void test_vector1()
{bit::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);cout << v.capacity() << endl;cout << v.size() << endl;
}
int main()
{test_vector1();return 0;
}

报错的原因在我们的reserve函数上:

 

 

我们画图进行解释:

 

 我们让_start指向tmp对应的空间:

 这时候,我们的_start和_finish不在同一块空间内,我们调用size函数:

 两块不同的空间进行相加减的值是未知的,我们接下来会对_finsh进行解引用,就会报错。

我们可以这样处理:

 我们可以先处理finish,画图进行解释:

我们先处理finish

 size函数表示_finish和_start相减,两个指针相减的结果是两个指针区域指向的相同数据的数目,是整型,我们让tmp+_finish.

 这时候,我们再把tmp赋值给_start。

这时候就没有什么问题了,我们继续测试:

 

 const修饰的begin和end

我们发现,begin和end都有两个接口函数,分别是const和非const。

 

 这两个const有什么区别吗?

答:第二个const:假如调用begin()函数的对象用const修饰了,我们就调用这个begin()函数。

第一个const:我们用const对象调用begin函数,返回的迭代器也用const修饰。

const iterator begin() const{return _start;}const iterator end() const{return _finish;}

const修饰的[]

const T&operator[](size_t pos) const{assert(pos < size());return _start[pos];}

empty函数:

bool empty() const{return _start == _finish;}

当_start和_finsh相等时,也就是当_start=_finsh=nullptr时,vector对象为空。

resize:

void resize(size_t n, T val = T()){if (n>capacity()){reserve(n);}if (n > size()){while (_finish < _start + n){*_finish = val;++_finish;}}else{_finish = _start + n;}}

 

这里的缺省值可以写0吗?

答:不行,对于内置类型,我们可以用0初始化,但是vector存储的数据既有可能是内置类型,也有可能是自定义类型,而对于自定义类型,我们不能用0去初始化。

 

 我们首先判断n是否大于capacity,如果大于我们需要进行扩容。

 判断n是否大于size,大于时需要填元素,我们调用循环把val填入到vector中。

 这里表示n小于size,所以我们需要元素,我们可以直接修改_finish的值,就能达到删除数据的目的。

尾删

void pop_back(){_finish--;}

我们直接让_finish--来删除数据。

不断的尾删会导致什么样的结果:

using namespace std;
#include<windows.h>
#include<iostream>
#include"vector.h"
void test_vector1()
{bit::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);while (1){v.pop_back();cout << v.size() << endl;Sleep(100);}
}
int main()
{test_vector1();return 0;
}

我们进行运行:

 因为我们的size是无符号类型,所以不断的进行尾删之后,size反而变成一个非常大的数据了。

我们调用库里面的尾删函数进行尝试:

using namespace std;
#include<vector>
#include<windows.h>
#include<iostream>
//#include"vector.h"
void test_vector1()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);while (1){v.pop_back();cout << v.size() << endl;Sleep(100);}
}
int main()
{test_vector1();return 0;
}

我们发现,库里面报的错误是断言错误,所以我们可以仿照库的写法对尾删函数进行完善:

 

void pop_back(){assert(_finish >= _start);_finish--;}

insert:

void insert(iterator pos, const T& val){if (_finish == _endofstorge){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;}

insert表示我们从pos位置处插入元素val。

为什么这里要加上const和&?

答:首先,必须加上引用,因为我们insert插入的极有可能是内置类型,也有可能是自定义类型,传参的过程本质是一个拷贝,对于自定义类型的深拷贝效率太低,消耗很大,所以我们通过传引用来传参,传引用的话我们就要限制函数内部,禁止对val进行修改,所以要加上const。

 

首先,我们要判断是否需要扩容。

 

我们的思路是这样的,先把pos位置后的数据全部往后面挪动一个位置,然后才填数据。 

 我们对insert函数进行实验:

using namespace std;
//#include<vector>
#include<windows.h>
#include<iostream>
#include<assert.h>
#include"vector.h"
void test_vector1()
{bit::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);bit::vector<int>::iterator it = v.end();v.insert(it, 15);for (auto s : v){cout << s << " ";}cout << endl;
}
int main()
{test_vector1();return 0;
}

我们在调试时发现两处异常:

异常1:

我们发现,编译器会一直执行while循环,不会终止。

异常2:

 

 我们进行的是尾插,pos与_finish的值却不相等。

这些问题是扩容的问题:

扩容导致了迭代器失效:

 因为我们只有我们的capacity和size都为4,我们要插入元素就需要扩容,我们的pos的值和_finish是相等的。

 

 我们的原空间被销毁,所以pos迭代器就失效了,pos和_finish无法比较大小,所以while死循环,pos位置已经被释放了所以无法被继续访问。

我们要做的就是更新pos:

void insert(iterator pos, const T& val){assert(pos <= _finish);assert(pos >= _start);if (_finish == _endofstorge){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;}

我们思考一个问题:insert之后的迭代器还能继续使用吗?

如代码所示:

 

答:最好不要,首先,只要我们因为调用insert而导致扩容,就会导致迭代器失效,it位置已经被释放。

许多人会有疑问:我们不是已经解决了迭代器失效了吗?

 

我们只是保证函数能正常使用,但是并没有保证迭代器没有失效:如图:

 

我们调用insert传参传递的it是传值传参,pos只是it的拷贝,pos值的修改并不影响it的变化,所以迭代器it还是失效了 。

那为什么insert函数不传引用传递第一个参数呢?

如图:

为什么不这样写呢?

 答:因为限制条件太多 ,例如:

 假如我们这样调用函数时,因为我们是传引用,我们的v.begin()是调用begin函数的返回值,是一个临时变量,临时变量具有常数属性,我们无法把临时变量当作参数调用insert函数。

所以,调用insert(pos,val)之后,pos对应的迭代器最好不要再使用。

标准库里面的find函数:

void test_vector1()
{bit::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);bit::vector<int>::iterator it = find(v.begin(), v.end(), 3);if (it != v.end()){v.insert(it, 30);}for (auto s : v){cout << s << " ";}cout << endl;
}

我们进行运行:

 

相关文章:

vector

目录 vector的成员函数&#xff1a; at&#xff1a; ​编辑 size&#xff1a; assign&#xff1a;赋值 insert find&#xff1f; erase swap shrink_to_fit ​编辑 vector的模拟实现&#xff1a; vector的框架&#xff1a; 构造函数&#xff1a; size和capacity r…...

LeetCode——104. 二叉树的最大深度

一、题目 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;https://leetcode.cn/problems/maximum…...

漫画 | Python是一门烂语言?

这个电脑的主人是个程序员&#xff0c;他相继学习了C、Java、Python、Go&#xff0c; 但是似乎总是停留在Hello World的水平。 每天晚上&#xff0c;夜深人静的时候&#xff0c;这些Hello World程序都会热火朝天地聊天但是&#xff0c;这一天发生了可怕的事情随着各个Hello wor…...

2023.2 新方案 java代码混淆 java加密 字符串加密

Java字节码可以反编译&#xff0c;特别是创业公司,很好的项目很容易被别人破解反编译,造成很严重的损失,所以本混淆方案能很好的保护源码,而且在不断迭代,增强混淆效果,异常问题处理,达到保护项目的目的&#xff1a; 本次升级包括: 2023年02年19日 : ht-confusion-project-1.8…...

Swift 周报 第二十三期

前言 本期是 Swift 编辑组自主整理周报的第十四期&#xff0c;每个模块已初步成型。各位读者如果有好的提议&#xff0c;欢迎在文末留言。 欢迎投稿或推荐内容。目前计划每两周周一发布&#xff0c;欢迎志同道合的朋友一起加入周报整理。 勇敢是即便知道好结局不会每每降临在…...

android系统屏幕旋转角度,应用界面横竖屏,设备旋转角度,三者的区别以及使用。

注意区分以下三种概念的区别&#xff01;&#xff01;&#xff01;。以及使用这三种方式判断横竖屏的方式。系统屏幕旋转角度fun getSystemRotation(): Int {val angle (getSystemService(WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation//系统屏幕旋转的角度值re…...

【华为云-开发者专属集市】DevCloud+ECS、MySQL搭建WordPress

文章目录AppBazaar官网选择与购买项目项目概况操作过程购买DevCloud服务创建项目添加制品库应用部署购买ECS添加部署模板并执行任务故障排除安装及访问WordPress登录网站管理后台访问网站完善部署模板资源释放使用总结AppBazaar官网 首先&#xff0c;我们来到AppBazaar的官网&…...

Milvus 群星闪耀时|又一个小目标达成 :社区正式突破 15,000 星!

如果把 Milvus 看作开源世界中的一束微光&#xff0c;那用户便是无垠宇宙中点点闪烁的星光。用户每一次点亮 star 之时&#xff0c;Milvus 就会迸发出更加耀眼的光芒。不知不觉&#xff0c;已有数以万计的 star 为 Milvus 而亮。2022 年 4 月&#xff0c;Milvus 在 GitHub 的 …...

Qt信号与槽使用方法总结

前言 在图形界面编程中QT是为首选&#xff0c;组件之间如何实现通信是核心的技术内容。Qt 使用了信号与槽的机制&#xff0c;非常的高效、简单、易学&#xff0c;方便开发者的使用。本文详细的介绍了Qt 当中信号与槽的概念&#xff0c;并演示了各种信号与槽的连接方式。 什么…...

SpringCloud alibaba-Sentinel服务降级策略

文章目录RT&#xff1a;异常比例&#xff1a;异常数&#xff1a;RT&#xff1a; 平均响应时间 (DEGRADE_GRADE_RT)&#xff1a;当 1s 内持续进入 N 个请求&#xff0c;对应时刻的平均响应时间&#xff08;秒级&#xff09;均超过阈值&#xff08;count&#xff0c;以 ms 为单位…...

python常用函数——random()函数

random() 返回随机生成的一个实数&#xff0c;范围在[0,1)之间 语法如下&#xff1a; import random random.random() # 注意&#xff1a;random()是不能直接访问的&#xff0c;需要导入random包&#xff0c;然后通过random静态对象调用 # 参数&#xff1a; 无 # 返回值 返回随…...

PX4之启动脚本

PX4通过rcS脚本来设定需要启动的程序&#xff0c;比如设备驱动、控制模块、数据通信等。rcS脚本在项目中的文件位置 ROMFS/px4fmu_common/rcS 对应硬件平台固件上的位置 /etc/init.d/rcS 启动脚本流程如下 #!/bin/sh # PX4FMU startup script. # # 一些注释 ## 设置默认参…...

Java零基础入门到精通(持续更新中)

打开CMD命令窗口 WINR输入cmd 常用cmd命令代码 切换磁盘 E: 回车即可切换到e盘查看当前路径下的所有内容 dir进入目录 cd test回退到上一级目录 cd..进入多级目录 cd test\index\aaa回退到磁盘目录 cd \清屏 cls关闭命令行窗口 exit小例子&#xff1a;使用命令行窗口…...

杂七杂八(12):Bilibili视频缓存 m4s音视频合并 shell脚本

视频目录结构如下&#xff1a; 267132000/ 267132000/c_1015740000/ 267132000/c_1015740000/entry.json 267132000/c_1015740000/80/ 267132000/c_1015740000/80/audio.m4s 267132000/c_1015740000/80/video.m4s267132000/c_1015740011/ 267132000/c_1015740011/entry.json 2…...

Qt 某光谱仪程序开发

某光谱仪程序开发 文章目录某光谱仪程序开发摘要安装驱动注册COM组件导出.h和.cpp在Qt 中添加源文件开发打包程序关键字&#xff1a; Demo、 Qt、 COM、 dumpcpp、 C摘要 今天接到一个临时小任务&#xff0c;写一个的项目子模块的Demo&#xff0c;以供和专家们交流一下项目技…...

蛋白质组学技术与常见分析培训班火热招生中!

什么是蛋白质组学&#xff1f; 蛋白质组学(proteomics)&#xff0c;是以蛋白质组为研究对象&#xff0c;研究细胞、组织或生物体蛋白质组成及其变化规律的科学。包括蛋白质的表达水平&#xff0c;翻译后修饰&#xff0c;蛋白与蛋白相互作用等研究内容&#xff0c;集中于动态描述…...

唤醒手腕 Java 后端 Springboot 框架结合 socketio 学习笔记

socketio 安装配置 Socket.IO是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架&#xff0c;它包括了客户端的JavaScript和服务器端的Node.js。 Socket.IO除了支持WebSocket通讯协议外&#xff0c;还支持许多种轮询&#xff08;P…...

C++入门:内联函数、auto关键字、基于范围for循环及指针空值nullptr

目录 一. 内联函数 1.1 内联函数的概念 1.2 内联函数的特性 1.3 内联函数和宏的优缺点对比 二. auto关键字&#xff08;C11&#xff09; 2.1 auto的功能 2.2 auto在使用时的注意事项 三. 基于范围的for循环&#xff08;C11&#xff09; 四. 指针空值nullptr&#xff08…...

Python遗传算法

1 人工智能概述 2020中国人工智能产业年会在苏州召开&#xff0c;会上发布的《中国人工智能发展报告2020》显示&#xff0c;过去十年(2011-2020) &#xff0c;中国人工智能专利申请量达389571件&#xff0c;占全球总量的74.7%&#xff0c;位居世界第一。 报告指出&#xff0c;…...

GEE学习笔记 六十四:绿色中国报告(个人版)

2019年上半年在遥感圈里最火的一篇文章莫过于这篇《China and India lead in greening of the world through land-use management》&#xff08;China and India lead in greening of the world through land-use management | Nature Sustainability&#xff09;&#xff0c;…...

如何用docker部署ELK?

环境&#xff1a; ELK 8.8.0 Ubuntu20.04 问题描述&#xff1a; 如何用docker部署ELK&#xff1f; 解决方案&#xff1a; 一、环境准备 &#xff08;一&#xff09;主机设置 安装 Docker Engine &#xff1a;版本需为 18.06.0 或更新。可通过命令 docker --version 检查…...

LLM:decoder-only 思考

文章目录 前言一、KV-cache1、为什么使用KV-cache2、KV-cache的运作原理 二、Decoder-only VS Encoder-Decoder1、Decoder-only2、Encoder-Decoder 三、Causal LM VS PrefixLM总结 前言 decoder-only模型是目前大模型的主流架构&#xff0c;由于OpenAI勇于挖坑踩坑&#xff0c…...

直线模组在手术机器人中有哪些技术挑战?

手术机器人在现代医疗领域发挥着越来越重要的作用&#xff0c;直线模组作为其关键部件&#xff0c;对手术机器人的性能有着至关重要的影响。然而&#xff0c;在手术机器人中使用直线模组面临着诸多技术挑战&#xff0c;具体如下&#xff1a; 1、‌高精度要求‌&#xff1a;手术…...

WSL 安装 Debian 12 后,Linux 如何安装 curl , quickjs ?

在 WSL 的 Debian 12 系统中安装 curl 非常简单&#xff0c;你可以直接使用 APT 包管理器从官方仓库安装。以下是详细步骤&#xff1a; 1. 更新软件包索引 首先确保系统的包索引是最新的&#xff1a; sudo apt update2. 安装 curl 执行以下命令安装 curl&#xff1a; sudo…...

【设计模式】责任链

【设计模式】责任链 在实际开发中&#xff0c;我们经常遇到这样的需求&#xff1a;某个请求需要经过多个处理者&#xff0c;但处理的顺序、方式可能会变化或扩展。这时候&#xff0c;责任链模式就能派上用场。 责任链模式&#xff08;Chain of Responsibility&#xff09; 是…...

RV1126-OPENCV 图像叠加

一.功能介绍 图像叠加&#xff1a;就是在一张图片上放上自己想要的图片&#xff0c;如LOGO&#xff0c;时间等。有点像之前提到的OSD原理一样。例如&#xff1a;下图一张图片&#xff0c;在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…...

解决 Win11 睡眠后黑屏无法唤醒的问题

目录 一、问题描述二、解决方法1. 禁用快速启动2. 设置 Management Engine Interface3. 允许混合睡眠其他命令 4. 修复系统文件5. 更新 Windows 或驱动程序6. 其他1&#xff09;更改电源选项2&#xff09;刷新 Hiberfil.sys 文件3&#xff09;重置电源计划4&#xff09;运行系统…...

java直接获取MyBatis将要执行的动态sql命令(不是拦截器方式)

目录 前言 一. 准备数据 1. 传输过来的json条件数据 2. mybatis 配置的动态sql 3. 想要的最终会执行的sql并返回给页面展示 二. 实现方式 三. 最终代码 前言 1.在平常开发过程中,MyBatis使用时非常多的,一般情况下我们只需要在控制台看看MyBatis输出的日志,要不就是实…...

[网页五子棋][匹配模块]前后端交互接口(消息推送机制)、客户端开发(匹配页面、匹配功能)

让多个用户&#xff0c;在游戏大厅中能够进行匹配&#xff0c;系统会把实力相近的两个玩家凑成一桌&#xff0c;进行对战 约定前后端交互接口 消息推送机制 匹配这样的功能&#xff0c;也是依赖消息推送机制的 玩家 1 点击开始匹配按钮&#xff0c;就会告诉服务器&#xff1…...

简历制作要精而不简

不得不说&#xff0c;不管是春招&#xff0c;还是秋招&#xff0c;我们在求职时&#xff0c;第一步便是制作一份简历。不得不承认&#xff0c;好的简历&#xff0c;就像一块敲门砖&#xff0c;能让面试官眼前一亮&#xff0c;让应聘成功的概率增添一分。 对于一个初次求职者来…...