vector以及迭代器失效

前言

        学习完string,之后学习的就是vector。vector就是之前C语言中讲到过的顺序表,用三个变量分别记录资源的位置,容器的容量和容器中元素个数。原来的写法是直接使用指针加两个int变量,而标准库中,三个都是由指针确定的,一个指向资源开头,一个指向资源容量,一个指向最后元素后一位。

        vector和string是相似的,vector相当于string中的char元素变为其他元素。vector相当于是用模版,将元素的类型范围扩大了。在函数的使用方面vector也和string极为相似,所以再介绍一次就好像炒闲饭一样。为了方便更多的读者了解vector,这里对于他的函数的使用还是会继续讲解一段。

一、vector简介

        vector是表示大小可以改变的数组的序列容器。
        与数组一样,vector对其元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针上的偏移量来访问其元素,并且与数组中的效率一样高。但与数组不同,它们的大小可以动态变化,其存储由容器自动处理。
        在内部,vector使用动态分配的数组来存储它们的元素。当插入新元素时,可能需要重新分配此数组以增加大小,这意味着分配一个新数组并将所有元素移动到其中。就处理时间而言,这是一项相对昂贵的任务,因此,向量不会在每次将元素添加到容器时重新分配。
        相反,vector容器可以分配一些额外的存储空间来适应可能的增长,因此容器的实际容量可能大于容纳其元素所需的存储空间(即其大小)。库可以实现不同的增长策略来平衡内存使用和重新分配,但在任何情况下,重新分配都应该只以对数增长的间隔发生,这样在向量末尾插入单个元素就可以以摊销的恒定时间复杂度提供(见push_back)。
        因此,与数组相比,vector消耗更多的内存,以换取管理存储和以高效方式动态增长的能力。
与其他动态序列容器(双端队列、列表和forward_lists)相比,vector访问其元素(就像数组一样)非常高效,并且从其末尾添加或删除元素也相对高效。对于涉及在末尾以外的位置插入或删除元素的操作,它们的性能比其他操作差,并且迭代器和引用的一致性不如列表和forward_lists。

二、vector内函数作用

        介绍完vector,我很还需要知道怎么去操作它。那么vector这个类里面究竟提供了什么函数供程序员使用呢?

1、构造函数

1.1、构造函数简介

        vector包括四种构造函数,他们的功能分别为:

(1) 空容器构造函数(默认构造函数)
        构造一个没有元素的空容器。
(2) 填充构造函数
        构造一个包含n个元素的容器。每个元素都是val的副本。
(3) 范围构造器
        构造一个包含与范围[first,last)一样多的元素的容器,每个元素都按照相同的顺序从该范围内的相应元素构造而成。
(4) 复制构造函数
        构造一个容器,其中包含x中每个元素的副本,顺序相同。

1.2、构造函数的使用举例

// vector的构造函数
#include <iostream>
#include <vector>void test_vector1()
{// 构造函数的使用顺序与上述相同:std::vector<int> first;                                // 默认构造std::vector<int> second (4,100);                       // 填充构造std::vector<int> third (second.begin(),second.end());  // 迭代器构造std::vector<int> fourth (third);                       // 拷贝构造// 迭代器构造函数也可用于从数组构造:int myints[] = {16,2,77,29};std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );std::cout << "The contents of fifth are:";for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';
}

        如上述代码所示,四种构造的使用方法,以及迭代器遍历。值得注意的是vector并没有像string那样能够直接的输出元素,这是因为作为字符串我们当然需要的只有直接输出,而vector中的元素不一定是连续输出有意义的数据。所以vector并没有重载流插入和输出的函数。

2、析构函数

2.1、析构函数简介

        作用,除了作用销毁vector中的内容,释放资源。

2.2、析构函数的使用举例

        结束了生命周期后自动调用,不做举例。

3、赋值重载

3.1、赋值重载函数简介

        为容器分配新内容,替换其当前内容,并相应地修改其大小。
        在调用之前,容器中保存的任何元素都会被分配或销毁。

3.2、复制重载的使用举例

// vector =重载
void test_vector2()
{std::vector<int> foo (3,0);std::vector<int> bar (5,0);bar = foo;foo = std::vector<int>();std::cout << "Size of foo: " << int(foo.size()) << '\n';std::cout << "Size of bar: " << int(bar.size()) << '\n';
}

        底层逻辑是创建临时变量进行深拷贝,然后与需要赋值的对象交换资源,最后临时变量析构,释放原来的资源。

4、vector的迭代器函数

4.1、迭代器简介

        begin()返回vector迭代器初始位置,end()返回vector迭代器最后位置。迭代器遵循左闭右开的规则,这是C++函数共通的。

        而rbegin()和rend()是反向迭代器的初始位置和结束位置。

        vector的迭代器支持“++”,“--”,“+n”,“-n”,是随机迭代器。

4.2、迭代器的使用举例

        这里参考构造函数的使用举例,里面有用到迭代器。迭代器在vector里实际上也是指针,不过之后做了升级,可能是由类封装的。

        至于迭代器失效,计算机能够检查出来的原因是因为新封装过。迭代器为何会失效,接下来的函数中会讲到。

5、有关容积的函数

5.1、容积函数简介

        容积函数包括以下7种,7中函数的功能与之前讲过的string中的同名函数效果一致,这里做一个简单介绍。

5.2、size()简介

        作用:返回vector中的元素数量。

        size()在之前函数中使用过,这里不再举例。resize()举例中有使用size()。

5.3、resize()简介和举例

        调整容器大小,使其包含n个元素。
        如果n小于当前容器大小,则内容将减少到其前n个元素,删除(并销毁)超出的元素。
        如果n大于当前容器大小,则通过在末尾插入尽可能多的元素来扩展内容,以达到n的大小。

        如果指定了val,则新元素将初始化为val的副本,否则将进行值初始化。
        如果n也大于当前容器容量,则会自动重新分配分配的存储空间。
        请注意,此函数通过插入或删除容器中的元素来更改容器的实际内容。

// resize()函数测试
void test_vector3()
{std::vector<int> myvector;// set some initial content:for (int i = 1; i < 10; i++) myvector.push_back(i);myvector.resize(5);myvector.resize(8,100);myvector.resize(12);std::cout << "myvector contains:";for (int i = 0; i < myvector.size(); i++)std::cout << ' ' << myvector[i];std::cout << '\n';
}

5.4、capacity()简介

        返回当前为向量分配的存储空间大小,以元素表示。
        该容量不一定等于vector大小。它可以相等或更大,额外的空间可以容纳增长,而不需要在每次插入时重新分配。
        请注意,此容量并不限制向量的大小。当这个容量耗尽并且需要更多时,它会被容器自动扩展(重新分配存储空间)。向量大小的理论极限由成员max_size给出。
        通过调用成员vector::reserve可以显式更改向量的容量。

        具体举例在下:

5.5、reserve()简介和举例

        要求vector容量至少足以包含n个元素。
        如果n大于当前vector容量,则该函数会使容器重新分配其存储空间,将其容量增加到n(或更大)。
        在所有其他情况下,函数调用不会导致重新分配,向量容量也不会受到影响。
        此函数对向量大小没有影响,也不能更改其元素。

// reserve()函数测试
void test_vector4()
{std::vector<int>::size_type sz;std::vector<int> foo;sz = foo.capacity();std::cout << "making foo grow:\n";for (int i = 0; i < 100; ++i) {foo.push_back(i);if (sz!=foo.capacity()) {sz = foo.capacity();std::cout << "capacity changed: " << sz << '\n';}}std::vector<int> bar;sz = bar.capacity();bar.reserve(100);   // this is the only difference with foo abovestd::cout << "making bar grow:\n";for (int i=0; i < 100; ++i) {bar.push_back(i);if (sz!=bar.capacity()) {sz = bar.capacity();std::cout << "capacity changed: " << sz << '\n';}}
}

        结果如图所示。

5.6、empty()简介

        返回向量是否为空(即其大小是否为0)。
        此函数不会以任何方式修改容器。要清除向量的内容,请参见vector::clear。

        此函数需要搭配其他函数使用,否则没有太大意义,其作用也如上所述,故不在举例。

5.7、其他

        这里还有两个函数没有讲到,是因为使用的时候作用不大,故不细讲。

6、有关元素访问的函数

6.1、元素访问的函数概括

        一共有5种访问元素的方法,这里front()函数是用来访问第一个元素的函数,back()是访问最后一个元素的函数,所以不继续做介绍,其他3个函数实用性更强,特别是[]重载。

6.2、operator[]与at()简介和举例

        返回对向量容器中位置n处元素的引用。
        一个类似的成员函数vector::at与此运算符函数具有相同的行为,除了vector::at被绑定检查,并通过抛出out_of_range异常来发出请求位置是否超出范围的信号。
        可移植程序不应使用超出范围的参数n调用此函数,因为这会导致未定义的行为。

        返回对向量中位置n处元素的引用。

// []重载测试
void test_vector5()
{std::vector<int> myvector (10);   // 在容器中填充10个0std::vector<int>::size_type sz = myvector.size();// 通过[]访问对应位置元素并修改for (unsigned i = 0; i < sz; i++) myvector[i]=i;// 利用[]旋转vector中的数据for (unsigned i = 0; i < sz / 2; i++){int temp;temp = myvector[sz-1-i];myvector[sz-1-i] = myvector[i];myvector[i] = temp;}std::cout << "myvector contains:";for (unsigned i = 0; i < sz; i++)std::cout << ' ' << myvector[i];std::cout << '\n';
}

6.3、data()介绍

        返回一个指向向量内部用于存储其自身元素的内存数组的直接指针。
        因为向量中的元素保证以与向量表示的顺序相同的顺序存储在连续的存储位置中,所以检索到的指针可以偏移以访问数组中的任何元素。

        返回指针后和数组的使用方法相同,所以不做举例,可以参抗[]重载的举例。

7、修改器类函数

7.1、修改器类函数简介

        这些功能效果和string中同名函数的效果相同。其中比较简单的有push_back():尾插一个元素到vector中,pop_back():删除最后一个元素。swap():交换两vector个容器中储存的内容。clear():清除vector中所有储存的数据。

        其他的函数将会在接下来的介绍中细讲。

7.2、insert()和erase()介绍和举例

        这里提出来将这两个函数的原因是因为,这两个函数有迭代器失效的风险。

        通过在指定位置的元素之前插入新元素来扩展向量,从而通过插入的元素数量有效地增加容器大小。
        这会导致自动重新分配分配的存储空间,如果并且仅当新的向量大小超过当前的向量容量。
        因为vector使用数组作为其底层存储,在向量端以外的位置插入元素会导致容器将位置之后的所有元素重新定位到新位置。与其他类型的序列容器(如list或forward_list)对同一操作执行的操作相比,这通常是一个低效的操作。
        这些参数决定插入多少个元素以及将它们初始化为哪些值:

// 测试vector中的insert()
void test_vector6()
{std::vector<int> myvector (3, 100);std::vector<int>::iterator it;it = myvector.begin();it = myvector.insert (it, 200);myvector.insert (it, 2, 300);// 因为使用了insert插入元素,所以上一个迭代器失效,需要更新它it = myvector.begin();std::vector<int> anothervector (2, 400);myvector.insert (it + 2, anothervector.begin(), anothervector.end());int myarray [] = { 501,502,503 };myvector.insert (myvector.begin(), myarray, myarray + 3);std::cout << "myvector contains:";for (it = myvector.begin(); it < myvector.end(); it++)std::cout << ' ' << *it;std::cout << '\n';
}

        迭代器失效的具体举例放到最后,接下来是erase()介绍和举例。

        从向量中删除单个元素(位置)或一系列元素([first,last))。
        这有效地减少了被移除的元素数量,从而减小了容器的大小。
        因为vector使用数组作为其底层存储,所以擦除向量末端以外位置的元素会导致容器在擦除段后将所有元素重新定位到新位置。与其他类型的序列容器(如list或forward_list)对同一操作执行的操作相比,这通常是一个低效的操作。

// vector中的erase()
void test_vector7()
{std::vector<int> myvector;// 在vector中存入10个元素for (int i = 1; i <= 10; i++) myvector.push_back(i);// 删除第6个元素myvector.erase (myvector.begin() + 5);// 继续删除前三个元素myvector.erase (myvector.begin(), myvector.begin() + 3);// 打印vector中剩余数据std::cout << "myvector contains:";for (unsigned i = 0; i < myvector.size(); ++i)std::cout << ' ' << myvector[i];std::cout << '\n';
}

        关于迭代器失效,是指之前使用过删除和插入之后,由于其他元素的位置已经改变,所以指针失去了原来的意义所以失效。比较危险的迭代器失效是数组在扩容的时候开辟了新空间,释放了旧空间,此时使用原来位置的迭代器就已经在访问野指针了。

// 迭代器失效
void test_vector8()
{std::vector<int> myvector(2, 10);std::vector<int>::iterator it = myvector.begin() + 1;std::cout << myvector.capacity() << '\n';for(int i = 0; i < 10; i++){myvector.insert(it, i);}std::cout << myvector.capacity() << '\n';for(auto e : myvector){std::cout << e << " ";}std::cout << '\n';
}

        目标是在固定位置插入10个元素,但是执行起来会出错,这是因为扩容导致迭代器的失效,出现了野指针访问,程序崩溃。除了插入,删除也有同样的问题。

// 迭代器失效
void test_vector8()
{std::vector<int> myvector(10, 10);std::vector<int>::iterator it = myvector.begin();// 本意是删除vector中所有偶数元素while(it != myvector.end()){if(*it % 2 == 0)myvector.erase(it);++it;}// 遍历vectorfor(auto e : myvector){std::cout << e << " ";}std::cout << '\n';
}

        这里的迭代器失效是因为删除了原来元素之后,it指向的位置虽然没变,但是下一个元素的位置前移了。而这里直接++it,使下一个元素没有检查到。

        对于检查更加细致的编译器,当使用迭代器插入或删除之后会告诉你迭代器失效,需要重新给它一个值。所以erase()和insert()都会返回一个迭代器,目的就是给程序员提供接口。

7.3、emplace()和emplace_back()介绍

        通过在位置插入新元素来扩展容器。这个新元素是使用args作为构造参数就地构造的。
        这有效地将容器大小增加了一个。
        当且仅当新的向量大小超过当前向量容量时,才会自动重新分配分配的存储空间。
        因为vector使用数组作为其底层存储,在向量端以外的位置插入元素会导致容器将位置后的所有元素移动一个到新位置。与其他类型的序列容器(如list或forward_list)执行的操作相比,这通常是一种低效的操作。有关直接在末尾扩展容器的成员函数,请参见emplace_back。

        通过调用allotor_traits::construct并转发args来就地构造元素。
        存在一个类似的成员函数insert,它可以将现有对象复制或移动到容器中。

        其实就相当于insert()的升级,可以在函数内部构造对象最后插入到vector中。

        emplace_back()则是固定了尾插而已。

        现阶段不要求掌握,但是以后会详细举例。

三、vector容器模拟

1、模拟

        模拟中函数的作用如上面介绍的一样,且尽量和库函数中的实现方法保持一致。

#include <iostream>
#include <assert.h>
#include <string>using namespace std;namespace lcs
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}// construct and destroyvector(int n = 4) // 默认构造:_start(new T[n]),_finish(_start),_end_of_storage(_start + n){}vector(int n, const T& value = T()):_start(new T[n]),_finish(_start),_end_of_storage(_start + n){for(size_t i = 0; i < n; ++i) // 指定初始值构造{push_back(value);}}template<class InputIterator>vector(InputIterator first, InputIterator last){while(first != last) // 迭代器深拷贝{push_back(*first);++first;}}vector(vector<T>& v):_start(new T[4]),_finish(_start),_end_of_storage(_start + 4){reserve(v.capacity());for(auto& e : v) // 放入每一个成员,深拷贝{push_back(e);}}vector<T>& operator=(vector<T> v){swap(v); // 将拷贝构造的值与前者交换return *this;}~vector(){if(_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}// capacitysize_t size() const  {return _finish - _start; // size = 指针相减}size_t capacity() const{return _end_of_storage - _start; // capacity = 指针相减}void reserve(size_t n){if(n > capacity()) // 如果容量小于设定值,就扩容,拷贝数据,修改内部指针{size_t old_len = size();T* tmp = new T[n];memcpy(tmp, _start, sizeof(T) * old_len);delete[] _start;_end_of_storage = tmp + n;_finish = tmp + old_len;_start = tmp;}}void resize(size_t n, const T& value = T()){if(n < size()) // 需要减小容量{_finish = _start + n;}else // 需要增加元素{reserve(n);while(_finish < _start + n){*_finish = value;++_finish;}}}///access///T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}///modify/void push_back(const T& x){assert(_start);// 扩容if(_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}// 储存数据*_finish = x;++_finish;}void pop_back(){assert(size());--_finish;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}iterator insert(iterator pos, const T& x){assert(pos <= _finish);// 扩容if(_finish == _end_of_storage){size_t len = pos - _start ;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}//挪动数据iterator end = _finish - 1;while(end >= pos){*(end + 1) = *end;--end;}*pos = x; // 添加数据++_finish; // 记录个数的增加return pos;}iterator erase(iterator pos){assert(pos < _finish);assert(size());//挪动数据iterator _pos = pos + 1;while(_pos < _finish){*(_pos - 1) = *(_pos);_pos++;}--_finish;return pos;}private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _end_of_storage; // 指向存储容量的尾};}

        模拟实现了大部分函数的功能,迭代器仍然是指针的重命名,接下来是对这些模块进行分块测试。

2、测试

#include "vector.hpp"// 测试自写vector
// 构造函数,迭代器,尾插尾删
void test_vector1()
{lcs::vector<int> v1(4, 1);lcs::vector<string> v2;v2.push_back("hello ");v2.push_back("world ");v2.push_back("nice for you");v1.pop_back();for(auto& e : v1){cout << e << " ";}cout << endl;for(auto& e : v2){cout << e << endl;;}
}// 测试插入删除,赋值
void test_vector2()
{lcs::vector<int> v1(4, 666);lcs::vector<int> v2(v1);v1.erase(v1.begin());v1.insert(v1.begin(), 81);v1.insert(v1.begin() + 2, 777);for(auto& e : v2){cout << e << " ";}cout << endl;v2 = v1;for(auto& e : v2){cout << e << " ";}cout << endl;
}// 测试[], capacity类函数
void test_vector3()
{lcs::vector<int> v1(4, 666);v1.erase(v1.begin());v1.insert(v1.begin(), 81);v1.insert(v1.begin() + 2, 777);cout << v1[2] << endl;cout << "capacity()->" << v1.capacity() << endl;v1.reserve(40);cout << "capacity()->" << v1.capacity() << endl;v1.reserve(20);cout << "capacity()->" << v1.capacity() << endl << endl;cout << "size()->" << v1.size() << endl;v1.resize(30);cout << "size()->" << v1.size() << endl;v1.resize(2);cout << "size()->" << v1.size() << endl;for(size_t i = 0; i < v1.size(); ++i){cout << v1[i] << " ";}cout << endl;
}int main()
{test_vector1();test_vector2();test_vector3();return 0;
}

        运行后结果与期望相同,虽然没有库中函数那么多重载,但是能全部实现出来其他函数也都不会有太大问题。

结语

        到这里博客的内容就告一段落了,这篇博客介绍函数的部分和上一篇介绍string的差不多,我也觉得有些冗余了。如果可以的话,下次将list会将相似的内容省略掉,直接进入正题将有差别的地方,然后模拟。

        list的话就有更多的地方要讲了,特别是迭代器部分,和vector、string这样连续空间的容器实现是完全不同的,需要重载函数。更为复杂,特别是const迭代器的实现,不容错过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50218.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql server 连接报错error 40

做个简单的记录,造成40 的原因有很多,你的错误并不一定就是我遇到的这种情况. 错误描述: 首先我在使用ssms 工具连接的时候是可以正常连接的,也能对数据库进行操作. 在使用 ef core 连接 Sql Server 时报错: Microsoft.Data.SqlClient.SqlException (0x80131904): A network-r…

hadoop学习(二)

一.MapReduce 1.1定义&#xff1a;是一个分布式运算程序的编程框架 1.2核心功能&#xff1a;将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上。 1.3优点 1&#xff09;易于编程 它简单的实现一些接口&#…

【Linux】Ubuntu解决Release 文件已经过期问题

​今天在执行update更新软件包时遇到了此问题&#xff1a;E: http://cn.archive.ubuntu.com/ubuntu/dists/jammy-updates/InRelease 的 Release 文件已经过期(已经过期了 247天 21小时 33分 15秒)。该仓库的更新将不会应用&#xff0c;如图 ​ 这个报错之前其实经常遇到&#x…

电脑虚拟摄像头软件分享|用手机打破电脑摄像头的极限

随着手机摄像头的不断更新迭代&#xff0c;手机已经接近专业电脑摄像头的画质。这让我们可以花费更低的成本获取优质的电脑录像画面。今天小编给大家详细讲解电脑虚拟摄像头的在我们日常生活中的妙用&#xff0c;以及分享几款口碑不错的电脑虚拟摄像头软件。有需要的小伙伴可以…

Unity3D 转换微信小游戏指引 05 广告内购

Unity3D 转换微信小游戏指引系列&#xff08;第五期 完结&#xff09; 广告 在小程序后台页面找到推广->流量主 开通条件如下&#xff1a; 开通之后&#xff0c;需要接入广告组件。 调用创建广告组件的接口时&#xff0c;需要传入参数 adUnitId&#xff0c;这个是开通流量…

「C++系列」数组

文章目录 一、数组1. 声明数组2. 初始化数组3. 访问数组元素4. 遍历数组注意事项示例代码 二、多维数组1. 声明二维数组2. 初始化二维数组3. 访问二维数组元素4. 遍历二维数组注意事项示例代码 三、指向数组的指针1. 声明指向数组的指针2. 通过指针访问数组元素3. 指针和数组的…

Android 10.0 framework默认沉浸式状态栏功能实现

1. 前言 在10.0的系统rom定制化开发中,在实现状态栏的某些定制化开发中,在某些产品需要实现沉浸式状态栏,就是需要app 能全屏显示同样也能显示状态栏,接下来就来分析下相关的功能实现 如图: 2.framework默认沉浸式状态栏功能实现的核心类 frameworks\base\core\java\andro…

【神经网络】梯度下降的优化方法【数学公式+代码示例】

文章目录 1、简介2、指数加权平均2.1、公式2.2、代码 3、Momentum⭐3.1、公式演变3.2、代码 4、AdaGrad4.1、计算步骤4.2、代码示例 5、RMSProp5.1、公式5.2、代码5.3、小结 6、Adam6.1、公式和步骤解释⭐6.2、代码⭐6.3、优点 7、何为鞍点8、小结 &#x1f343;作者介绍&#…

国防科技大学深圳地区新生欢送会圆满举行

2024年7月28日&#xff0c;第97个八一建军节来临之际&#xff0c;在这个充满希望的盛夏时节&#xff0c;深圳地区迎来了13名即将踏入国防科技大学的优秀学子。 为了庆祝这一荣耀时刻&#xff0c;并表达对新生的深切祝福&#xff0c;在国防科技大学深圳校友会黄丹会长的积极倡议…

书生大模型实战营--L1关卡-Llamaindex RAG实践

一、安装llamaindex库 pip install llama-index pip install llama-index-embeddings-huggingface 二、问2024年巴黎奥运会 中国队获得几枚金牌&#xff0c;无法回答该问题 三、构建Llamaindex RAG 1、初始化llm 2、构建词向量模型 下载模型&#xff1a;git clone https://…

基于k8s快速搭建docker镜像服务的demo

基于k8s快速搭建docker镜像服务的demo 一、环境准备 如标题&#xff0c;你需要环境中有和2个平台&#xff0c;并且服务器上也已经安装好docker服务 接下来我来构建一个docker镜像&#xff0c;然后使用harbork8s来快速部署服务demo 二、部署概述 使用docker构建镜像&#x…

(2024,通用逼近定理(UAT),函数逼近,Kolmogorov–Arnold定理(KAT),任意深度/宽度的网络逼近)综述

A Survey on Universal Approximation Theorems 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. 神经网络&#xff08;NN&#xff09; 3. 通用逼近定理&#xff0…

我的NAS是怎么连接Amazon Web Services S3的

作为IT爱好者&#xff0c;很多家庭都配备了Network Attached Storage&#xff08;NAS&#xff09;&#xff0c;用于存储和管理大量数据。一个常见的挑战是如何实现异地备份&#xff0c;以确保数据的安全性和可恢复性。以下是一些解决方案和工具&#xff0c;可以帮助用户有效地管…

“手撕”MySQL的索引

目录 二、索引的作用 三、索引的缺点 四、如何使用索引 查看索引&#xff1a; 创建索引&#xff1a; ​编辑 删除索引&#xff1a; 五、索引的底层原理 那什么是B树&#xff0c;什么是B树呢&#xff1f; B树的好处&#xff1a; 总结&#xff1a; 一、什么是索引 索…

C语言的周末小练习(贰)

周末小练习&#xff1a; 5、输入身高和体重&#xff0c;计算BMI指数(BMI w/(h*h))。 #include <stdio.h>int main() {float w,h,BMI;/*printf("请输入体重\n");scanf("%f",&w);printf("请输入身高\n");scanf("%f",&h…

vue3 快速入门 (五) : Flex布局

1. 如何变成Flex布局 变成Flex容器&#xff0c;只需在容器布局的节点的CSS中&#xff0c;增加display : flex .mylayout {/* 省略了其他代码 */display: flex; }2. flex direction : 方向 row : 以行排列 row-reverse &#xff1a; 以行反向排列 column &#xff1a;以列排列…

Matlab编程资源库(11)多项式计算

一、多项式的四则运算 1&#xff0e;多项式的加减运算 2&#xff0e;多项式乘法运算 函数conv(P1,P2)用于求多项式P1和P2的乘积。 这里&#xff0c;P1、P2是两个多项式系数向量。 3&#xff0e;多项式除法 函数[Q,r]deconv(P1,P2)用于对多项式P1和P2作除法运算。其中Q返回多项…

【前端 09】JavaScript中的对象与JSON

JavaScript中的对象与JSON 在JavaScript中&#xff0c;对象和JSON&#xff08;JavaScript Object Notation&#xff09;是两个紧密相连但又有区别的概念。它们都在数据处理和交换中扮演着重要角色。本文将详细讲解JavaScript中的自定义对象以及JSON对象的基本概念、格式、用法…

基于微信小程序+SpringBoot+Vue的教务管理系统(带1w+文档)

基于微信小程序SpringBootVue的教务管理系统(带1w文档) 基于微信小程序SpringBootVue的教务管理系统(带1w文档) 在目前的情况下&#xff0c;可以引进一款基于web的高校教务管理系统这样的现代化管理工具&#xff0c;这个工具就是解决上述问题的最好的解决方案。它不仅可以实时完…

python拼接字符串方法

文章目录 1. 使用加号&#xff08;&#xff09;2. 使用str.join()方法3. 使用格式化字符串&#xff08;f-strings, % 操作符, .format() 方法&#xff09;4. 使用列表推导式和join()结合 性能对比 在Python中&#xff0c;字符串拼接是将两个或多个字符串合并成一个新字符串的过…