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

[C++]——vector

 🌇个人主页:_麦麦_ 

📚今日小句:快乐的方式有很多种,第一种便是见到你。

 

目录

一、前言

二、vector的介绍及使用

2.1 vector的介绍

2.2 vector的使用

2.2.1 vector的定义(构造函数)

2.2.2 vector迭代器的使用

 2.2.3 vector 空间增长问题

2.2.3vector增删查改

2.2.4 vector迭代器失效问题

三、vector的模拟实现

3.1成员变量与构造函数

3.2 insert与reverse

3.3 析构函数

3.4 【】的重载

3.5 insert

3.5 erase

3.6 pop_back

 3.7 resize

3.8  其他构造

3.9 赋值重载

四、全部代码

五、结语

一、前言

        今天为大家带来vector的介绍和模拟实现,文章若有不足之处,欢迎大家给出指正!

二、vector的介绍及使用

2.1 vector的介绍

1. vector是表示可变大小数组的序列容器。

2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。

6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。

2.2 vector的使用

2.2.1 vector的定义(构造函数)

(construct)构造函数声明接口说明
vector()无参构造
vector(size_type n, const value_type& val = value_type())

构造并初始化n个val

vector(const vector& x)拷贝构造
vector((InputIterator first, InputIterator last)使用迭代器进行初始化构造

2.2.2 vector迭代器的使用

ieterator的使用接口说明
begin+end获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置 的iterator/const_iterator
rbegin+rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator

 

 

         对于正向迭代器来言,通过begin()和end()获得的区间是一个左闭右开的区间,也就是end()所返回的迭代器指向的vector的最后一位有效数据的下一位

        对于反向迭代器,我们知道通过它我们能够反向的遍历vector这个容器,但是它的实现并不只是简单地将正向迭代器头尾进行交换,通过上图我们也能够发现,具体的原因和实现我们会在下面的模拟实现为大家娓娓道来。

 2.2.3 vector 空间增长问题

容量空间接口说明
size

获取数据个数

capacity获取容量大小
empty判断是否为空
resize改变vector的size
reserve改变vector的capacity

        首先,我们要知道的是对于vector的这个容器,当我们在进行插入数据,并不需要手动扩容,因为它会自动的根据容器中_size和_capacity两者的比较来判断在插入数据前容器是否已满,如果已满就会将该容器扩容,但具体的扩容大下则是根据不同的编译环境有着不同的实现,有的是按照1.5倍_capacity来进行扩容,有的则是按照2倍_capacity来进行扩容。

        其次,除了vector自行的扩容会对vector的容量进行改变,它还提供了两个与容器大小有关的接口resize和reserve来供我们使用。对于前者,按字面意义来说就是改变vector的_size。当我们使用这个函数的时候大抵会面对三种种情况:1.输入量>容量 2.有效数据<输入量<容量 3.输入量<有效数据。

        对于第一种情况,编译器便会对当前容器进行扩容的操作。而对于第二情况,由于当前容器的capacity能够满足我们对size的需求,就会在当前有效数据后填补我们所输入的数据至size。最后一种情况的下,由于我们输入的size小于有效数据,那么我们便要减小有效数据的数量至输入的_size。

        最后呢,是reserve这一个接口。它最主要的作用就是能够帮助我们开辟空间,以减少容器的不断扩容,需要注意的一点是当我们的输入容量小于目前容量时,一般情况下,编译器并不会将多余的容量的进行释放。这是因为当我们释放空间的时候,只能释放一整块空间,而不能释放一块空间的一小部分。否则代价会极大。

        

ps:reserve负责开辟空间,而resize在开空间的同时还会进行初始化,影响size

2.2.3vector增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样访问

2.2.4 vector迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器, 程序可能会崩溃)。 对于vector可能会导致其迭代器失效的操作有:

①会引起其底层空间改变的操作,都可能使迭代器失效,比如:resize、reserve、insert、assign、push_back等

指定位置元素的删除操作-erase。erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

注:与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

#include <iostream>
using namespace std;
#include <vector>//底层空间改变引起的迭代器失效
void text_1()
{vector<int> v(10, 5);auto it = v.begin();//将有效元素个数增加到150个,多出的位置用2来填充,操作期间底层会扩容//v.resize(150, 2);//reserve的做优就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变//v.reserve(150);//插入元素期间可能引起扩容,而导致原空间被释放//v.push_back(8);//v.insert(v.begin(), 8);//给vector重新赋值,可能会引起的层容量改变v.assign(150, 8);/*出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。*/while (it != v.end()){cout << *it;++it;}cout << endl;
}//使用erase导致的迭代器失效
void text_2()
{vector<int> v = {1,2,3,4,5};auto it = find(v.begin(), v.end(), 4);v.erase(it);cout << *it << endl;}int main()
{//text_1();text_2();return 0;
}

三、vector的模拟实现

3.1成员变量与构造函数

        当我们最开始了解一个容器的时候,最先研究的就是它的成员变量与构造函数,所以当我们在模拟实现的时候,自然也是从此开始着手。

        首先是vector的成员变量,它的成员变量可是与string是不一样的,string是一个char*的指针加上size和capacity,而vector的则是由三个迭代器组成,分别是指向起始位置有效数据的下一位存储的最大容量

        然后是构造函数,我们会模拟实现常见的三种构造函数,分别是无参的构造,n个值的构造以及一段迭代器区间构造。至于其中涉及的函数,会在下面具体介绍,

        代码如下:

namespace mine
{template <class T>class vector{typedef T* iterator;typedef const T* const_iterator;public:vector():_start(nullptr),_finish(nullptr),_endofstorage(nullptr){}vector(size_t n, const T& val = T()){resize(n, val);}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}private:iterator _start=nullptr;iterator _finish=nullptr;iterator _endofstorage=nullptr;};}

 

 

3.2 insert与reverse

        在能够能够成功创建出一个vector对象,接下来就是实现数据的插入了 ,我们先来实现最简单的尾插。 在进行数据的尾插之前,我们第一步要做的就是检查当前容器的容量,如果容量已满,那么我们就需要对当前的容器进行扩容,也就是reverse的操作。

        具体代码如下:

        void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];size_t i = 0;while (i != size()){tmp[i] = _start[i];++i;}delete[] _start;/*		if (_start){memcpy(tmp, _start, sizeof(T)*size());delete[] _start;}*/_start = tmp;_finish = tmp + sz;_endofstorage = tmp + n;}}void push_back(const T& x){//扩容if (_finish == _endofstorage){size_t newcapacity = (_start == nullptr ? 4 : 2 * (_finish - _start));reserve(newcapacity);}//插入数据*_finish= x;++_finish;}

3.3 析构函数

        对于vector的析构函数,我们只需要将申请的空间释放,并将成员变量定义为空指针即可,具体代码如下: 

~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}

3.4 【】的重载

        对于vector来说,它也重载了【】操作符,我们只需对_start迭代器进行处理即可得到我们想要的数据。

        具体代码如下:

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

3.5 insert

         由于vector的存储空间是连续的,因此当我们进行数据插入的时候,尾插的效率是最高的,当然,有时我们也不可避免的会遇到头插或者在中间插入数据的情况。于是便提供了insert的这个接口。

        在本节中我们实现的是在指定位置位置插入一个有效数据,那么在实现这个成员函数的时候倘若一不小心,就会碰到我们在前面讲过的迭代器失效的问题。为什么会迭代器失效呢,因为当我们在进行数据的插入时,如果容器内的数据已满,就需要进行扩容的操作,那么扩容之后,传进来的迭代器位置就有可能并不指向容器位置,因而导致了迭代器失效。那么为了解决这个问题,当我们在扩容之后,就需要重新对传进来的迭代器进行一个重新的赋值,保证其仍指向容器的数据。为了做到这一点,我们需要的便是在进行扩容之间先记录pos迭代器的相对位置。

        具体实现如下:

        void insert(iterator pos ,const T& x){assert(pos >=_start && pos<=_endofstorage);size_t dis = pos - _start;//扩容if (_finish == _endofstorage){size_t newcapacity = (_start == nullptr ? 4 : 2 * (_finish - _start));reserve(newcapacity);//避免pos迭代器失效pos = _start + dis;}//插入数据{size_t len = _finish-pos;T* end = _finish;//挪动数据while (end >= pos){*(end + 1) = *end;--end;}//插入数据*pos = x;++_finish;}

3.5 erase

        在实现完vector的插入和尾插后,那么当我们想要删除某些数据的时候,就要用到erase这个函数了,在这面我们实现的仅仅是简单地删除指定位置的一个有效数据。

        对于erase而言,也会面临的pos迭代器失效的问题,当我们删除的是尾部最后一个数据的时候,那么面对这样的情况,我们的解决方法就是给erase函数增加一个返回值,返回值是指向删除数据的下一个数据的迭代器,那么当我们在使用这个函数的时候只要去接受这个返回值来对pos进行更新就不会出现迭代器失效的问题了。

        具体代码如下: 

	    iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator tmp = pos + 1;while (tmp != _finish){*(tmp - 1) = *tmp;++tmp;}--_finish;return pos;}

3.6 pop_back

        在实现完erase后,尾删的话我们只需要复用这个函数即可,对尾部的迭代器进行--即可实现尾删的功能。

        具体代码如下: 

    	void pop_back(){erase(end()-1);}

 3.7 resize

        在实现完reverse之后,紧随其后的就是resize。在前面我们已经提到了,在对vector进行resize的时候我们会面临三种情况:1.输入量>容量 2.有效数据<输入量<容量 3.输入量<有效数据。

        对于第一种情况,编译器便会对当前容器进行扩容的操作。而对于第二情况,由于当前容器的capacity能够满足我们对size的需求,就会在当前有效数据后填补我们所输入的数据至size。最后一种情况的下,由于我们输入的size小于有效数据,那么我们便要减小有效数据的数量至输入的_size。

        具体实现如下:

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

3.8  其他构造

        在前面较为基础的构造之外,接下来我们来实现vector的其他构造。

        首先是拷贝构造,一种是传统的写法,就是开好空间后,利用memcpy将另一个对象拷贝过去,另一种则是复用尾插,将数据一个个插入进去。对于前者的话会存在一个浅拷贝的问题,vector中存储的是内置类型的话,memcpy是没有问题的,但是如果是自定义类型的话就会存在一个浅拷贝的问题,这是由于memcpy是按字节进行拷贝的,为了解决这个问题,我们在进行扩容的的时候,就不能再使用memcpy的方式来拷贝数据,而是通过赋值的方式来调用自定义类型的赋值重载,这样就实现了深拷贝。

       然后是n个值来初始化vector,思路大致就是复用我们已经写好的resize就可以了

       最后就是用一段迭代器区间来初始化vector,实现起来主要就是在用一个类模板,不过在实际当我们在使用n个值来初始化vector的时候,会出现匹配的歧义,它会与迭代器的这个构造更加匹配,因此面对匹配歧义这个问题,我们可以再重载一个n个值来初始化的构造函数,只需要将前面的类型修改为int即可。

        具体代码如下:

       //传统写法vector(const vector& v){_start = new T[v.capacity()];_finish = _start + v.size();_endofstorage = _start + v.capacity();size_t n = 0;while (n != size()){*(_start + n) = *(v.begin() + n);++n;}}//复用写法vector(const vector& v){reserve(v.capacity());for (auto e : v){push_back(e);}}vector(size_t n, const T& val = T()){resize(n, val);}vector(int n, const T& val = T()){resize(n, val);}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}

3.9 赋值重载

        在实现vector的赋值重载我们依旧可以采取像前面string的类似方法,通过与形参交换成员变量来达到赋值拷贝的效果。

        具体代码如下: 

	    void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage,v._endofstorage);}vector<T>&  operator=( vector<T> v){swap(v);return *this;}

四、全部代码

#pragma once
#include <assert.h>namespace mine
{template <class T>class vector{typedef T* iterator;typedef const T* const_iterator;public:vector():_start(nullptr),_finish(nullptr),_endofstorage(nullptr){}vector(size_t n, const T& val = T()){resize(n, val);}vector(int n, const T& val = T()){resize(n, val);}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];size_t i = 0;while (i != size()){tmp[i] = _start[i];++i;}delete[] _start;/*		if (_start){memcpy(tmp, _start, sizeof(T)*size());delete[] _start;}*/_start = tmp;_finish = tmp + sz;_endofstorage = tmp + n;}}void push_back(const T& x){//扩容if (_finish == _endofstorage){size_t newcapacity = (_start == nullptr ? 4 : 2 * (_finish - _start));reserve(newcapacity);}//插入数据*_finish= x;++_finish;}void insert(iterator pos ,const T& x){assert(pos >=_start && pos<=_endofstorage);size_t dis = pos - _start;//扩容if (_finish == _endofstorage){size_t newcapacity = (_start == nullptr ? 4 : 2 * (_finish - _start));reserve(newcapacity);//避免pos迭代器失效pos = _start + dis;}//插入数据{size_t len = _finish-pos;T* end = _finish;//挪动数据while (end >= pos){*(end + 1) = *end;--end;}//插入数据*pos = x;++_finish;}}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator tmp = pos + 1;while (tmp != _finish){*(tmp - 1) = *tmp;++tmp;}--_finish;return pos;}void pop_back(){erase(end()-1);}void resize(size_t n, const T& val = T()){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}}//传统写法/*	vector(const vector& v){_start = new T[v.capacity()];_finish = _start + v.size();_endofstorage = _start + v.capacity();size_t n = 0;while (n != size()){*(_start + n) = *(v.begin() + n);++n;}}*///复用写法vector(const vector& v){reserve(v.capacity());for (auto e : v){push_back(e);}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage,v._endofstorage);}vector<T>&  operator=( vector<T> v){swap(v);return *this;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}private:iterator _start=nullptr;iterator _finish=nullptr;iterator _endofstorage=nullptr;};void print(const vector<int>& v){for (auto e : v){cout << e<<' ';}cout << endl;}
}

五、结语

         到此为止,关于vector的讲解就告一段落了,至于其他的内容,小伙伴们敬请期待呀!

        关注我 _麦麦_分享更多干货:_麦麦_-CSDN博客
        大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见! 

相关文章:

[C++]——vector

&#x1f307;个人主页&#xff1a;_麦麦_ &#x1f4da;今日小句&#xff1a;快乐的方式有很多种&#xff0c;第一种便是见到你。 目录 一、前言 二、vector的介绍及使用 2.1 vector的介绍 2.2 vector的使用 2.2.1 vector的定义&#xff08;构造函数&#xff09; 2.2.2…...

自动驾驶:LQR、ILQR和DDP原理、公式推导以及代码演示(七、CILQR约束条件下的ILQR求解)

&#xff08;七&#xff09;CILQR约束条件下的ILQR求解 CILQR&#xff08;(Constrained Iterative Linear Quadratic Regulator)&#xff09; 是为了在 iLQR 基础上扩展处理控制输入和状态约束的问题。在这种情况下&#xff0c;系统不仅要优化控制输入以最小化代价函数&#x…...

随想录笔记-二叉树练习题

合并二叉树 617. 合并二叉树 - 力扣&#xff08;LeetCode&#xff09; dfs递归 class Solution {public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {if(root1null||root2null){return root1null?root2:root1;}return dfs(root1,root2);}public TreeNode dfs(Tre…...

华雁智科前端面试题

1. var 变量的提升 题目&#xff1a; var a 1 function fun() {console.log(b)var b 2 } fun() console.log(a) 正确输出结果&#xff1a;undefined、1答错了&#xff0c;给一个大嘴巴子&#xff0c;错误答案输出结果为&#xff1a;2,1 此题主要考察 var 定义的变量&…...

【iOS】单例模式

【iOS】单例模式 什么是单例模式&#xff1f; 定义 单例模式&#xff0c;简单地说就是一个类只对应一个对象&#xff0c;每次使用这个类时&#xff0c;都只能获取到那一个对象。它的详细定义如下&#xff1a; 如果一个类始终只能创建一个实例&#xff0c;则这个类被称为单例…...

Linux | 探索 Linux 信号机制:信号的产生和自定义捕捉

信号是 Linux 操作系统中非常重要的进程控制机制&#xff0c;用来异步通知进程发生某种事件。理解信号的产生、阻塞、递达、捕捉等概念&#xff0c;可以帮助开发者更好地编写健壮的应用程序&#xff0c;避免由于未处理的信号导致程序异常退出。本文将带你从基础概念开始&#x…...

递归的时间复杂度分析

确定回溯算法的时间复杂度通常比较复杂&#xff0c;因为它取决于搜索空间的大小以及你的剪枝效率。对于生成从1到n的所有长度为k的组合。分析这类算法的时间复杂度时&#xff0c;我们通常需要考虑递归树的所有可能路径。 组合数 生成的组合数量是从n个元素中选择k个的组合数&…...

C++: 二叉树进阶面试题

做每件事之前都心存诚意, 就会事半功倍. 目录 前言1. 根据二叉树创建字符串2. 二叉树的层序遍历Ⅰ3. 二叉树的层序遍历Ⅱ4. 二叉树的最近公共祖先5. 二叉搜索树与双向链表6. 根据一棵树的前序遍历与中序遍历构造二叉树7. 根据一棵树的中序遍历与后序遍历构造二叉树8. 二叉树的…...

【HarmonyOS NEXT】实现网络图片保存到手机相册

【问题描述】 给定一个网络图片的地址&#xff0c;实现将图片保存到手机相册 【API】 phAccessHelper.showAssetsCreationDialog【官方文档】 https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#showassetscreationdialog…...

Pytorch详解-数据模块

Pytorch详解-数据模块 torch.utils.data.Dataset数据交互模块—Dataset的功能示例系列APIsconcatSubsetrandom_splitsampler unsqueeze DataLoaderDataLoader功能支持两种形式数据集读取自定义采样策略自动组装成批数据多进程数据加载自动实现锁页内存&#xff08;Pinning Memo…...

浅谈openresty

熟悉了nginx后再来看openresty&#xff0c;不得不说openresty是比较优秀的。 对nginx和openresty的历史等在这此就不介绍了。 首先对标nginx&#xff0c;自然有优劣 一、开发难度 nginx&#xff1a; 毫无疑问nginx的开发难度比较高&#xff0c;需要扎实的c/c基础&#xff…...

【学习笔记】2024最新版SpringCloud教程

2024最新版SpringCloud教程 0 前言闲聊开篇简介 1 SpringBoot和SpringCloud版本选型 2 SpringCloud是什么能干吗 3 SpringCloud各组件的停更升级替换说明 4 项目实战之需求说明 5 项目实战之Maven父工程聚合说明和mysql驱动选择 6 项目实战之Mapper4一键生成Dao层代码 …...

Proxyless Service Mesh:下一代微服务架构体系

一、项目背景及意义 在当今的微服务架构中&#xff0c;应用程序通常被拆分成多个独立的服务&#xff0c;这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性&#xff0c;但也带来了新的挑战&#xff0c;比如&#xff1a; 服务间通信的复杂性&#…...

大数据Flink(一百一十八):SQL水印操作(Watermark)

文章目录 ​​​​​​SQL水印操作&#xff08;Watermark&#xff09; 一、为什么要有WaterMark 二、​​​​​​​Watermark解决的问题 三、​​​​​​​​​​​​​​代码演示 ​​​​​​SQL水印操作&#xff08;Watermark&#xff09; 一、​​​​​​​为什么要…...

【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动

把QGroundControl地面站添加到Ubuntu侧边菜单栏启动 简介准备工作步骤 1: 创建 Desktop Entry 文件步骤 2: 编辑 Desktop Entry 文件步骤 3: 刷新应用程序菜单步骤 4: 将 QGroundControl 固定到侧边栏 环境&#xff1a; Ubuntu &#xff1a;20.04 LTS 简介 QGroundControl 是…...

PostgreSQL配置主从同步

PostgreSQL配置主从同步 1 主、备库安装postgresql软件 su - pg12 cd /home/pg12/resource tar -zxvf postgresql-12.9.tar.gz cd postgresql-12.9/ ./configure --prefix/home/pg12/soft/ make -j 16 && make install2 主、备库配置环境变量 vi ~/.bash_profile…...

基于python+django+vue的鲜花商城系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…...

李飞飞任CEO,空间智能公司World Labs亮相,全明星阵容曝光

人工智能的下个大方向已经出现&#xff0c;标志性学者决定下场创业。 本周五&#xff0c;一个重磅消息引爆了 AI 圈&#xff1a;斯坦福大学计算机科学家李飞飞正式宣布创办 AI 初创公司 ——World Labs&#xff0c;旨在向人工智能系统传授有关物理现实的深入知识。 李飞飞说道&…...

PyTorch详解-可视化模块

PyTorch详解-可视化模块 Tensorboard 基础与使用启动 TensorBoard访问 TensorBoard使用 TensorBoardSummaryWriter类介绍参数说明常用方法 CNN卷积核与特征图可视化参数说明返回值 混淆矩阵与训练曲线可视化混淆矩阵可视化训练曲线绘制 模型参数打印参数说明输出解释 Tensorboa…...

Bootstrap 警告信息(Alerts)使用介绍

本章将讲解警告&#xff08;Alerts&#xff09;以及 Bootstrap 所提供的用于警告的 class。警告&#xff08;Alerts&#xff09;向用户提供了一种定义消息样式的方式。它们为典型的用户操作提供了上下文信息反馈。 您可以为警告框添加一个可选的关闭按钮。为了创建一个内联的可…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...