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

vector的模拟实现

文章目录

  • vector的模拟实现
    • vector 结构定义
    • 1. vector的迭代器的实现
    • 2. vector四个默认成员函数
      • 2.1 构造函数
        • 2.1.1 无参
        • 2.1.2 n个val初始化
        • 2.1.3 迭代器初始化
      • 2.2 析构函数
      • 2.3 拷贝构造函数
        • 2.3.1 传统写法
        • 2.3.2 现代写法
      • 2.4 赋值重载运算符
    • 3. 管理数组相关接口
      • 3.1 reserve
      • 3.2 resize
      • 3.3 push_back
      • 3.3 pop_back
      • 3.4 empty
      • 3.5 insert
      • 3.6 erase
      • 3.7 size
      • 3.8 capacity
      • 3.9 swap
    • 4. 运算符重载
      • 4.1 []
    • 5. 迭代器失效问题
    • 6. 深浅拷贝问题
      • 6.1 简单介绍深浅拷贝
      • 6.2 vector中深浅拷贝的几种情况

vector的模拟实现

vector 结构定义

vector要实现成模板的形式, 使vector中可以存储任意类型的数据; 成员变量不再像string给一些具体的数据类型, 而是采用指针模拟的迭代器来实现, 这里使用C+11语法给成员变量缺省值, 在构造和拷贝构造时不用去初始化列表中初始化, 直接用缺省值

在这里插入图片描述

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start=nullptr;             //指向首元素 iterator _finish=nullptr;            //sizeiterator _end_of_storage=nullptr;    //capacity
}

1. vector的迭代器的实现

vector的迭代器有两种, 一种迭代器可读指向内容可以修改, 一种是const类型的迭代器,只读不可修改指向内容。用指针实现即可。

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;          }//指向内容不能修改, 只能读不能写const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}
}

2. vector四个默认成员函数

2.1 构造函数

按照库中的构造函数, 我们提供3个版本的

在这里插入图片描述

2.1.1 无参

vector()          //给缺省值  --- 初始化列表使用
{}

2.1.2 n个val初始化

直接向数组中尾插数据,用reserve提前扩容, 提高效率

注意点: 这里传参的第二个参数使用匿名对象,const T& val = T() 这种写法会调用默认构造(可以是任意类型),我们前面讲内置类型是没有默认构造函数的, 理论而言是没有的, 但是调用模板之后必须要支持默认构造

补充匿名对象

在这里插入图片描述

vector(size_t n, const T& val = T())    //匿名对象
{reserve(n);     //提前扩容, 提高效率for (int i = 0; i < n; ++i){push_back(val);}
}

2.1.3 迭代器初始化

这里又要使用模板实现, 要实现一个任意类型的迭代器允许任意类型的数据使用,直接用迭代器遍历数组, 尾插数据即可

//任意类型的迭代器   --- 允许类的成员函数再设函数模板
//[first, last)
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}

2.2 析构函数

直接将start指向的空间释放, 并且将三个指针置空即可

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

2.3 拷贝构造函数

2.3.1 传统写法

开辟新的空间,使即将被赋值的对象 _start指向新空间,后赋值(深拷贝)将原空间内容拷贝到新空间, 改变新空间的 _finish和 _end_of_storage

一定要使用赋值的方法深拷贝, memcpy是浅拷贝

vector(const vector<T>& v)            
{_start = new T[v.capacity()];//memcpy(_start, v._start, sizeof(T) * v.size());//深拷贝for (size_t i = 0; i < v.size(); ++i){_start[i] = v._start[i];      //赋值就是深拷贝}_finish = _start + v.size();_end_of_storage = _start + v.capacity();
}

2.3.2 现代写法

直接用迭代区间初始化去构造临时对象 tmp ,然后交换tmp 和原对象即可

vector(const vector<T>& v)           
{vector<T> tmp(v.begin(), v.end());swap(tmp);
}

2.4 赋值重载运算符

这里要实现深拷贝, v1=v2其实是v2去拷贝构造v,然后交换v1和v,便可对v1赋值

//v1 = v2
vector<T>& operator=(vector<T>  v)     //v2拷贝构造v, 交换v1和v
{swap(v);return *this;
}

3. 管理数组相关接口

3.1 reserve

在n>capacity时再去扩容, 这种做法是防止缩容

  1. 提前记录好原数组大小, 防止扩容拷贝数据后数组大小改变;开辟n个空间大小的tmp数组
  2. 遍历将原数组中元素拷贝到新数组中, 一定要使用赋值的方法深拷贝,不能使用memcpy(memcpy是浅拷贝)
  3. 释放原数组,改变3个指针的指向
void reserve(size_t n)
{if (n > capacity())      //防止缩容{size_t sz = size();  //提前记录好原数组大小, 防止扩容拷贝数据后数组大小改变T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * size());   //错误写法,memcpy浅拷贝for (size_t i = 0; i <sz; ++i){tmp[i] = _start[i];      //赋值就是深拷贝}delete[] _start;}_start = tmp;_finish = _start+sz;_end_of_storage = _start + n;}
}

3.2 resize

  1. n < size() 就是删除数据,直接改变 _finish的指向即可
  2. n > size()调用reserve函数扩容, 后遍历给数组赋值
void resize(size_t n, T val=T())       // T val=T()  调用默认构造(可以是任意类型)
{if (n < size())       //删除数据{_finish = _start + n;}else if (n > size()){if (n > capacity())reserve(n);//初始化while (_finish != _start + n){*_finish = val;++_finish;}}
}

3.3 push_back

  1. 检查容量, 容量不够时需要扩容
  2. 直接向 _finsh指向位置插入元素, 后改变 _finish的指向
void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}

3.3 pop_back

直接finish指向前移, – _finsh即可

注意: 数组为空无法删除

void pop_back()
{assert(!empty());--_finish;
}

3.4 empty

直接判断 _start是否等于 _finish

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

3.5 insert

  1. 检查容量,观察是否需要扩容, 扩容前计算出pos与start之间,pos与start之间相对距离不变,扩容后更新pos位置(这里存在迭代器失效的问题)
  2. 遍历挪动数据
  3. 将val插入pos位置

注意: 检查pos位置的合法性

iterator insert(iterator pos, const T&val)
{assert(pos >= _start   &&  pos<=_finish);//1. 检查容量if (_finish == _end_of_storage){size_t len = pos - _start;      //pos与start之间相对距离不变reserve(capacity() == 0 ? 4 : capacity() * 2);//扩容后更新pos, 解决pos失效的问题pos = _start + len;}//2. 挪动数据iterator end = _finish - 1;while (end>=pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}

3.6 erase

  1. 遍历挪动数据
  2. 改变 _ finish的指向

注意: 检查pos位置的合法性

iterator erase(iterator pos)
{assert(pos >= _start && pos < _finish);//挪动数据iterator start = pos + 1;while (start != _finish){*(start - 1) = *start;++start;}--_finish;return pos;        //返回被删除元素的下一个元素, 对迭代器进行更新
}

3.7 size

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

3.8 capacity

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

3.9 swap

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}

4. 运算符重载

4.1 []

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

5. 迭代器失效问题

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

对于vector可能会导致其迭代器失效的操作有:

1. 指定位置前插入元素操作 — insert

如下代码, 错误演示:

void insert(iterator pos, const T&val)
{assert(pos >= _start   &&  pos<=_finish);//1. 检查容量if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}//2. 挪动数据iterator end = _finish - 1;while (end>=pos){*(end + 1) = *end;--end;}*pos = val;++_finish;
}

此代码存在两个大问题:

  1. 迭代器失效的问题,插入元素扩容时, 可能存在异地扩容, 扩容后pos的指向没有更新, 就可能会出现如下图扩容后pos处指向随机值,扩容后无法找到pos指向的元素**。所以我们利用pos和 _start之间的相对距离不变的特点, 在扩容前先算出pos和 _start之间的距离,扩容后更新pos指向即可**
  2. 函数形参pos是实参的临时拷贝, 形参的改变不会影响外部的实参。有两种结局方式: 1. 引用传参, iterator begin() 迭代器实现是传值返回, 会返回临时对象, 临时对象具有常性这里相当于权限放大 2. 按照库里的实现方式给返回值。这里只能使用给返回值的方式更新pos

正确代码,就是上面单独实现的insert

在这里插入图片描述

在这里插入图片描述

2. 指定位置元素的删除操作–erase

如下代码

void test_vector4()
{std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//auto pos = find(v1.begin(), v1.end(), 2);      //程序可能不会崩溃auto pos = find(v1.begin(), v1.end(), 4);        //一定会崩溃if (pos != v1.end()){v1.erase(pos);}(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;
}
  1. 对于删除元素2来说, 删除2后, 去更改*pos位置的值,VS下程序会崩溃, Linux下不会

  2. 对于删除元素4来说, 删除4后, 去更改*pos位置的值,VS和Linux下都会崩溃, 删除pos位置后, 该位置已不再数组的范围中了, 再去更改访问就是野指针的问题了,程序一定会崩溃

分析:

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了

结论: erase之后, pos是否失效? 失效,不要访问,行为结果未定义

删除元素后, 要对迭代器指向位置进行更新, 防止迭代器失效

如下代码, 删除数组中的偶数元素, 删除后要对it进行更新,所以erase删除元素后返回的是删除元素的下一个位置

void test_vector5()
{vector<int> v1;v1.push_back(10);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);v1.push_back(50);vector<int>::iterator it = v1.begin();while (it != v1.end()){if (*it % 2 == 0)//v1.erase(it);it = v1.erase(it);    //每次删完后更新itelseit++;}for (auto e : v1){cout << e << " ";}cout << endl;
}

6. 深浅拷贝问题

6.1 简单介绍深浅拷贝

浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

深拷贝

深拷贝: 给每个对象独立分配资源, 保证多个对象之间不会因资源而造成多次释放,造成程序崩溃问题

在这里插入图片描述

6.2 vector中深浅拷贝的几种情况

情况1: 不写拷贝构造函数, 直接使用默认的拷贝构造函数

代码如下, 实现将v1拷贝到v2, 不写拷贝构造函数使用默认的拷贝构造函数时,默认的拷贝构造会对内置类型完成浅拷贝, 会使v1,v2指向同一块空间, 并且析构时会析构两次, 程序会崩溃。

在这里插入图片描述

情况2: 写了拷贝构造函数, 拷贝构造函数中使用memcpy和reserve中的memcpy

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

代码如下, 将字符串拷贝到一个字符串数组中, 使用memcpy实现了外层深拷贝,内层浅拷贝, 程序会崩溃

在这里插入图片描述

在使用memcpy函数前, 实现了外层的深拷贝

在这里插入图片描述

使用memcpy函数后, 实现了内层的浅拷贝

在这里插入图片描述

我们要实现的是外层深拷贝,内层深拷贝, 直接 使用赋值的方法实现深拷贝

在这里插入图片描述

在这里插入图片描述

情况3: 拷贝构造函数中使用赋值深拷贝,不写赋值重载运算符, 使用默认生成的

代码如下, 实现打印杨辉三角,运行此代码程序会崩溃

在这里插入图片描述

调试发现实现的是外层深拷贝,内层浅拷贝

在这里插入图片描述

在拷贝构造时外层的vv和ret实现了深拷贝后, 内层的 vector< int > 调用默认的赋值重载, 实现的是浅拷贝, 而我们的内层需要实现深拷贝,写赋值重载函数,内层用交换来实现深拷贝

在这里插入图片描述

相关文章:

vector的模拟实现

文章目录vector的模拟实现vector 结构定义1. vector的迭代器的实现2. vector四个默认成员函数2.1 构造函数2.1.1 无参2.1.2 n个val初始化2.1.3 迭代器初始化2.2 析构函数2.3 拷贝构造函数2.3.1 传统写法2.3.2 现代写法2.4 赋值重载运算符3. 管理数组相关接口3.1 reserve3.2 res…...

【无标题】compose系列教程-4.相对布局ConstraintLayout的使用

相对布局在Compose中被称为ConstraintLayout&#xff0c;它可以让您以相对于其他元素的方式放置元素。 以下是使用ConstraintLayout实现相对布局的示例代码&#xff1a; Composable fun ConstraintLayoutExample() { ConstraintLayout(modifier Modifier.fillMaxSize()…...

JavaEE简单示例——Bean管理

简单介绍&#xff1a; 在这一章节我们会比较详细的介绍我们在之前的测试类中以及Bean管理XML配置文件中所使用到的类和方法&#xff0c;以及XML中配置的属性所代表的详细含义。以及之前我们反复提到但是一直没有详细的讲解的一个东西&#xff1a;容器。我们可以大致的有一个概…...

react+antdpro+ts实现企业级项目四:注册页面实现及useEmotionCss的介绍

创建文件路径并注册register路由 在pages/User下创建Register文件夹并创建index.tsx文件 然后在config/routes创建register注册路由。注册完后&#xff0c;当在登陆页面点击注册按钮时就可以跳转到此注册页面而不会报404了。 export default [{path: /user,layout: false,rou…...

Shifu基础功能:数据采集

数据采集 我们可以通过HTTP/gRPC与deviceShifu进行通信&#xff0c;deviceShifu会将我们发送的请求转换成设备所支持协议的形式&#xff0c;并发送给设备。 当设备接收到指令之后&#xff0c;数据会传输到deviceShifu中&#xff0c;之后deviceShifu将数据作为我们请求的返回值…...

代码随想录算法训练营day54 | 动态规划之子序列 392.判断子序列 115.不同的子序列

day54392.判断子序列1.确定dp数组&#xff08;dp table&#xff09;以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组115.不同的子序列1.确定dp数组&#xff08;dp table&#xff09;以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺…...

MCAL知识点(三):Port与Dio配置

目录 1、概述 2、 Port的EB-tresos配置 2.1、创建模块 2.2 General配置 2.3、PortCnfigSet 2.4、Port的属性...

初识C++需要了解的一些东西(1)

目录&#x1f947;命名空间&#x1f3c5;存在原因&#x1f3f5;命名空间定义&#x1f3a7;命名空间的3种使用方式&#x1f3c6;C输入和输出&#x1f31d;缺省参数&#x1f31c;缺省参数概念⭐️缺省参数分类☀️函数重载&#x1f525;引用&#x1f31a;引用概念&#x1f313;引…...

友元函数的使用大全

概述 我们知道&#xff0c;C的类具有封装和信息隐藏的特性。一般情况下&#xff0c;我们会封装public的成员函数供用户调用&#xff0c;而将成员变量设置为private或protected。但在一些比较复杂的业务情况下&#xff0c;可能需要去访问对象中大量的private或protected成员变量…...

QT学习笔记-QT多项目系统中如何指定各项目的编译顺序

QT学习笔记-QT多项目系统中如何指定各项目的编译顺序背景环境解决思路具体操作背景 为了更好的复用程序功能以及更优雅的管理程序&#xff0c;有经验的程序员通常要对程序进行分层和模块化设计。在QT/C这个工具中同样可以通过创建子项目的方式对程序进行模块化&#xff0c;在这…...

JWT令牌解析及刷新令牌(十一)

写在前面&#xff1a;各位看到此博客的小伙伴&#xff0c;如有不对的地方请及时通过私信我或者评论此博客的方式指出&#xff0c;以免误人子弟。多谢&#xff01;如果我的博客对你有帮助&#xff0c;欢迎进行评论✏️✏️、点赞&#x1f44d;&#x1f44d;、收藏⭐️⭐️&#…...

Hibernate学习(一)

Hibernate学习&#xff08;一&#xff09; Hibernate框架的概述&#xff1a; 一&#xff1a;什么是框架&#xff1a;指软件的半成品&#xff0c;已经完成了部分功能。 二&#xff1a;EE的三层架构&#xff1a; 1.EE的三层经典架构&#xff1a; 我在这里主要学的是ssh框架。 三…...

Go的 context 包的使用

文章目录背景简介主要方法获得顶级上下文当前协程上下文的操作创建下级协程的Context场景示例背景 在父子协程协作过程中, 父协程需要给子协程传递信息, 子协程依据父协程传递的信息来决定自己的操作. 这种需求下可以使用 context 包 简介 Context通常被称为上下文&#xff…...

微服务为什么要用到 API 网关?

本文介绍了 API 网关日志的价值&#xff0c;并以知名网关 Apache APISIX 为例&#xff0c;展示如何集成 API 网关日志。 作者程小兰&#xff0c;API7.ai 技术工程师&#xff0c;Apache APISIX Contributor。 原文链接 什么是微服务 微服务架构&#xff08;通常简称为微服务&a…...

SWUST OJ 1042: 中缀表达式转换为后缀表达式【表达式转逆波兰表达式】

题目描述 中缀表达式是一个通用的算术或逻辑公式表示方法&#xff0c;操作符是以中缀形式处于操作数的中间&#xff08;例&#xff1a;3 4&#xff09;&#xff0c;中缀表达式是人们常用的算术表示方法。后缀表达式不包含括号&#xff0c;运算符放在两个运算对象的后面&#…...

Matlab基础知识

MATLAB批量读入文件和导出文件一、 批量读入文件1.若文件名称有序&#xff0c;则按照文件名称规律循环读取文件(1)读入不同的excelfor i1:1:10strstrcat(F:\数据\v,int2str(i),.xlsx); %连接字符串形成文件名Axlsread(str); end注&#xff1a;变量i为整数时&#xff0c;可以用i…...

动手学深度学习【2】——softmax回归

动手学深度学习网址&#xff1a;动手学深度学习 注&#xff1a;本部分只对基础知识进行简单的介绍并附上完整的代码实现&#xff0c;更多内容可参考上述网址。 前言 前面一节我们谈到了线性回归&#xff0c;它解决的是预测某个值的问题。但是在日常生活这&#xff0c;除了预测…...

深入理解Activity的生命周期

之前学习安卓的时候只是知道生命周期是什么&#xff0c;有哪几个&#xff0c;但具体的详细的东西却不知道&#xff0c;后来看过《Android开发艺术探索》和大量博客之后&#xff0c;才觉得自己真正有点理解生命周期&#xff0c;本文是我对生命周期的认识的总结。废话少说先上图。…...

Go语言刷题常用数据结构和算法

数据结构 字符串 string 访问字符串中的值 通过下标访问 s1 : "hello world"first : s[0]通过切片访问 s2 : []byte(s1) first : s2[0]通过for-range循环访问 for i, v : range s1 {fmt.Println(i, v) }查询字符是否属于特定字符集 // 判断字符串中是否包含a、b、…...

深入vue2.x源码系列:手写代码来模拟Vue2.x的响应式数据实现

前言 Vue响应式原理由以下三个部分组成&#xff1a; 数据劫持&#xff1a;Vue通过Object.defineProperty()方法对data中的每个属性进行拦截&#xff0c;当属性值发生变化时&#xff0c;会触发setter方法&#xff0c;通知依赖更新。发布-订阅模式&#xff1a;Vue使用发布-订阅…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...