C++STL——vector类与模拟实现

news2024/11/23 8:58:51

vector类

  • vector
    • 常用接口介绍
      • 初始化
      • reserve与resize
      • assign
      • 缩容接口
    • 算法库中的find
    • vector的底层小部分框架
  • 模拟实现vectot
    • 模拟vector的整体代码
    • 迭代器失效问题
    • 深层深浅拷贝问题

vector

vector是表示可变大小数组的序列容器,就像数组一样,采用连续存储空间来存储元素,功能和数组类似,但是vector可以管理动态内存,并且在vector中的元素可以是自定义类型。
vector的文档介绍:
在这里插入图片描述
在这里插入图片描述
arr与str中已经存放进了我们初始化的数据。

常用接口介绍

这里很多都和string的接口相似,就不一一介绍了

初始化

vector (const allocator_type& alloc = allocator_type());//无参构造函数
参数:是库里面写的空间配置器组件
vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());//构造并且初始化n个val
参数:第一个是数据数量,第二个是数据本身,第三个是空间配置器组件
template <class InputIterator>
vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());//使用迭代器进行初始化构造
参数:第一个是头第二个是尾,第三个是空间配置器组件
vector (const vector& x);//拷贝构造

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int>arr;
	for (int i = 0; i < arr.size(); i++)
	{
		cout << arr[i];
	}
	cout << endl;
	vector<int>arr1 = { 1,2 };
	for (int i = 0; i < arr1.size(); i++)
	{
		cout << arr1[i];
	}
	cout << endl;
	vector<int>arr2(5, 2);
	for (int i = 0; i < arr2.size(); i++)
	{
		cout << arr2[i];
	}
	cout << endl;

	vector<int>arr3(arr1);
	for (int i = 0; i < arr3.size(); i++)
	{
		cout << arr3[i];
	}
	cout << endl;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	vector<int>arr4(arr.begin(), arr.end());
	for (auto e : arr4)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

reserve与resize

在这里插入图片描述
这个和string的类似,都是先开辟一处指定空间大小,但是不会缩容:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	cout << arr.capacity() << endl;
	arr.reserve(10);//size并不会变大
	cout << arr.capacity() << endl;
	arr.reserve(3);
	cout << arr.capacity() << endl;//不缩容
	return 0;
}

在这里插入图片描述

在这里插入图片描述
这里要注意一下,val这个参数是一个模板,缺省值是一个匿名的模板的对象,因为传过来的不一定就是内置类型,很有可能是自定义类型。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	arr.push_back(5);

	arr.resize(8);//空位补0
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl << arr.capacity() << endl;
	arr.resize(15, 1);//空位补1
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl << arr.capacity() << endl;

	arr.resize(3);//减小长度但不缩容
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl << arr.capacity() << endl;
	return 0;
}

在这里插入图片描述

assign

在这里插入图片描述

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	for (auto e : arr)
	{
		cout << e << ' ';
	}
	cout << endl;
	arr.assign(10, 1);
	for (auto e : arr)
	{
		cout << e << ' ';
	}
	cout << endl;
	vector<int>arr1;
	arr1.push_back(4);
	arr1.push_back(5);
	arr1.push_back(6);
	arr1.push_back(7);
	arr.assign(arr1.begin(), arr1.end());
	for (auto e : arr)
	{
		cout << e << ' ';
	}
	cout << endl;
	string str("hello world");
	arr.assign(str.begin(), str.end());
	for (auto e : arr)
	{
		cout << e << ' ';
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

缩容接口

最好不要缩容,因为vector设计理念就是不动空间,空间换时间,代价很大。
在这里插入图片描述
和原来一样,需要开辟一块新空间,然后释放掉原来的空间,再进行拷贝,此函数是为了节省空间。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int>arr;
	arr.push_back(0);
	arr.push_back(0);
	arr.push_back(0);
	arr.reserve(10);
	cout << arr.capacity() << endl;
	arr.shrink_to_fit();
	cout << arr.capacity() << endl;

	return 0;
}

在这里插入图片描述

算法库中的find

查看文档发现vector中并没有查找的函数,是但是算法库为STL中提供了一个查找的函数,不然每一个容器都要写查找岂不是很麻烦?
在这里插入图片描述
模板是类模板,函数的参数使用类模板与迭代器实现的。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	arr.push_back(5);

	vector<int>::iterator p = find(arr.begin(), arr.end(), 3);
	if(p != arr.end())
		arr.insert(p, 10);//在3的位置进行头插
	for (auto e : arr)
	{
		cout << e << ' ';
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

vector的底层小部分框架

在模拟实现string的时候,成员变量有三个,存储字符串的空间位置,此对象的字符串有效长度大小和有效空间大小。
如果去看库中实现的vector源码,我们会发现成员变量最主要的有三个:
在这里插入图片描述
一是start,指向了这组数的开头。
二是finish,指向有效数据的最后一个位置的下一个位置,这样与start相减就是有效数据的长度了。
三是end_of_storage,指向了有效空间的末尾。
类型是* iterator。

模拟实现vectot

模拟vector的整体代码

#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;
namespace baiye
{
    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 begin() const
        {
            return _start;
        }

        const_iterator end() const
        {
            return _finish;
        }
        
        // construct and destroy

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

        vector(size_t n, const T& value = T())
            : _start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            reserve(n);
            for (size_t i = 0; i < n; i++)
            {
                push_back(value);
            }
        }
        vector(int n, const T& value = T())//这里有一个参数n为int类型是防止传参是两个整形,如果是size_t需要隐式类型转换
            : _start(nullptr)              //这会导致调用的不是这个构造函数而是带有模板的构造函数了
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            reserve(n);
            for (size_t i = 0; i < n; i++)
            {
                push_back(value);
            }
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last)//模板的优先级大于隐式类型转换
            : _start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }

        vector(const vector<T>& v)
            : _start(nullptr)
            , _finish(nullptr)
            , _endOfStorage(nullptr)
        {
            vector<T>gcc(v.begin(), v.end());
            swap(gcc);
        }

        vector<T>& operator= (vector<T> v)
        {
            swap(v);
            return *this;
        }

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

        // capacity

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

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

        void reserve(size_t n)
        {
            if (n > capacity())
            {
                T* p = new T[n];
                int a = size();
                if (_start)
                {
                    for (int i = 0; i < a; i++)
                    {
                        p[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = p;
                _finish = _start + a;
                _endOfStorage = _start + n;
            }
        }

        void resize(size_t n, const T& value = T())
        {
            if (n > capacity())
            {
                reserve(n);
            }
            if (n > size())
            {
                while (_finish != _start + n)
                {
                    *_finish = value;
                    _finish++;
                }
            }
            else
            {
                _finish = _start + n;
            }
        }
        ///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)
        {
            if (_finish == _endOfStorage)
            {
                int sum = capacity() == 0 ? 4 : 2 * capacity();
                reserve(sum);
            }
            *_finish = x;
            _finish++;
        }
        bool empty() const
        {
            return  !(_finish - _start);
        }
        void pop_back()
        {
            assert(!empty());
            _finish--;
        }

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

        iterator insert(iterator pos, const T& x)
        {
            assert(pos <= _finish);
            assert(pos > _start);
            if (_finish == _endOfStorage)
            {
                int n = pos - _start;
                int sum = capacity() == 0 ? 4 : 2 * capacity();
                reserve(sum);
                pos = _start + n;
            }
            iterator p1 = _finish;
            while (p1 > pos)
            {
                *p1 = *(p1 - 1);
                p1--;
            }
            *pos = x;
            ++_finish;
            return pos;
        }

        iterator erase(iterator pos)
        {
            assert(pos >= _start);
            assert(pos < _finish);
            iterator p = pos + 1;
            while (p < _finish)
            {
                *(p - 1) = *p;
                p++;
            }
            _finish--;
            return pos;
        }
        void clear()
        {
            _finish = _start;
        }
    private:

        iterator _start; // 指向数据块的开始

        iterator _finish; // 指向有效数据的尾

        iterator _endOfStorage; // 指向存储容量的尾

    };

}

不过,在模拟vector的过程中,最致命的问题有两个。

迭代器失效问题

在实现到vector的接口insert时,我写的代码是这样的。

iterator insert(iterator pos, const T& x)
{
    assert(pos <= _finish);
    assert(pos > _start);
    if (_finish == _endOfStorage)
    {
        int sum = capacity() == 0 ? 4 : 2 * capacity();
        reserve(sum);
    }
    iterator p1 = _finish;
    while (p1 > pos)
    {
        *p1 = *(p1 - 1);
        p1--;
    }
    *pos = x;
    ++_finish;
    return pos;
}

在测试的时候发现结果是这样的:
在这里插入图片描述
然后我进行了调试发现:
在这里插入图片描述
上面是最初的位置。
然后向下走,需要扩容。
在这里插入图片描述
当扩容之后我们发现,vector的成员变量地址都变了,但是pos指向的还是原来的位置,导致pos指向的内容也就变成了我们上面看到的随机值。
这就是扩容需要重新开辟一块空间并且释放掉原来的空间导致的迭代器失效问题。
我们传过来的参数pos是没有重新分配空间的地址,那么在扩容时失效应该如何避免呢?
这里只需要记录原来pos与_start的距离,然后重新让pos指向有效位置即可。

iterator insert(iterator pos, const T& x)
{
    assert(pos <= _finish);
    assert(pos > _start);
    if (_finish == _endOfStorage)
    {
    	int n = pos - _start;
        int sum = capacity() == 0 ? 4 : 2 * capacity();
        reserve(sum);
        pos = _start + n;
    }
    iterator p1 = _finish;
    while (p1 > pos)
    {
        *p1 = *(p1 - 1);
        p1--;
    }
    *pos = x;
    ++_finish;
    return pos;
}

在这里插入图片描述
迭代器失效归根结底就是野指针的问题。
那么再来看这一段代码:

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;
	vector<int>::iterator p = find(arr.begin(), arr.end(), 3);
	if (p != arr.end())
		arr.insert(p, 5);//这里会发生扩容
	p++;//这里再次对于p进行改动会怎么样?
	return 0;
}

在这里插入图片描述
这里也是野指针的问题,所以这里记得利用返回值。

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;
	vector<int>::iterator p = find(arr.begin(), arr.end(), 3);
	if (p != arr.end())
		p = arr.insert(p, 5);
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;

	(*p)++;//这里再次对于p进行改动会怎么样?
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;
	return 0;
}

至于为什么insert这个接口的函数pos参数为什么不是引用,这是因为如果是引用就要考虑传值时候的权限放大与缩小的问题了。
现在来看看第二种迭代器失效的问题:
在实现erase接口的时候代码是这样写的:

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

测试了一下没什么问题:
在这里插入图片描述
那么如果这样呢(这里先用库里面的vector来测试)

int main()
{
	vector<int>arr;
	arr.push_back(1);
	arr.push_back(2);
	arr.push_back(3);
	arr.push_back(4);
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;
	vector<int>::iterator p = find(arr.begin(), arr.end(), 3);
	arr.erase(p);
	for (auto e : arr)
	{
		cout << e;
	}
	cout << endl;
	p++;
	return 0;
}

在这里插入图片描述
那么这里也是野指针的问题。
这是为什么呢?我们的这个对象并没有重新分配地址啊。
在g++下这里是并没有报错的,而VS这里报错说明底层的实现原理是不同的。
这里失效时更好一点的,如果是尾删的话就是失效了。
在这里插入图片描述
所以按照库里面来返回删除元素的下一个元素的位置就好了。

深层深浅拷贝问题

注意我的操作:

int main()
{
	baiye::vector<baiye::vector<int>>arr;
	baiye::vector<int>cpp(10, 1);
	arr.push_back(cpp);
	arr.push_back(cpp);
	arr.push_back(cpp);
	arr.push_back(cpp);

	for (int i = 0; i < arr.size(); i++)
	{
		for (int j = 0; j < cpp.size(); j++)
		{
			cout << arr[i][j] << ' ';
		}
		cout << endl;
	}
	return 0;
}

在这里插入图片描述
OK,暂时没问题,再尾插一个cpp呢?
在这里插入图片描述
没让你失望,他崩了。
为什么4个不崩5个崩呢?
因为这也和重新开辟空间有关,我们来调试看看:
我直接快进到第五次尾插cpp

在这里插入图片描述
这里看好他们的地址:
在这里插入图片描述
这里地址p的成员地址和this指针中的成员地址相同了,这是为何?
图解:
在这里插入图片描述
这里是压入前四个的cpp的状态,此时并没有发生扩容。
当压入第五个cpp的时候发生扩容,我们看到代码是运行到memcpy的地方出问题的。
我们都知道memcpy拷贝是一个字节一个字节进行拷贝的,这里拷贝的是对标类型为vector<int>类型的_start进行开辟空间的p,那么拷贝的就是_start和p所指向位置的所有内容。
但是他们的成员都是指针,拷贝的都是地址编号,这就导致了新开辟的空间的前四个内容的成员指针指向了旧空间的成员指向的内容。
在这里插入图片描述
那么,下一步就是释放掉原来的旧空间了,释放的位置是_start指向的位置,这也就导致p空间中的前四个类型指向的空间也同样被释放掉了。
这就是整个问题的所在之处,解决问题方法就是不让他一个字节一个字节拷贝,可以用赋值来避免自定义类型的这种情况,如下:

void reserve(size_t n)
{
    if (n > capacity())
    {
        T* p = new T[n];
        int a = size();
        if (_start)
        {
            for (int i = 0; i < a; i++)
            {
                p[i] = _start[i];//这里改成赋值,就是调用拷贝构造去了
            }
            delete[] _start;
        }
        _start = p;
        _finish = _start + a;
        _endOfStorage = _start + n;
    }
}

在这里插入图片描述

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

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

相关文章

Three.js初识:渲染立方体、3d字体、修改渲染背景颜色

用场景对three.js进行渲染&#xff1a;场景、相机、渲染器 const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );const renderer new THREE.WebGLRenderer(); renderer.setSize( window.i…

[附源码]Python计算机毕业设计Django基于web的建设科技项目申报管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【内网安全】——Linux信息收集

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

ContentResolver.query流程分析

文章目录1.Context.getContentResolver()2.ContentResolver.query()3.ContentProviderProxy.query()4.Transport.query()总结增删改查ContentProvider时&#xff0c;通过Binder实现ContentProvider在App进程启动时进行实例化&#xff0c;具体时机是在Application.onCreate()执行…

项目构建生命周期与插件

项目构建生命周期描述的是一次构建过程经历了多少个事件。 maven对项目构建的生命周期划分为3套&#xff1a; clean&#xff1a;清理工作。 default&#xff1a;核心工作&#xff0c;例如编译、测试、打包、部署等。 site&#xff1a;产生报告&#xff0c;发布站点等。 clean生…

工具-Obsidian生产力工具,安装第三方插件(GitHub)教程,以安装Syntax Highlight(代码高亮)为例

文章目录1、去GitHub上找到你需要的插件2、下载到本地3、在obsidian中新建文件4、将下载好的GitHub文件放置文件夹5、obsidian中设置6、插入代码块实例1、去GitHub上找到你需要的插件 在GitHub的搜索框中&#xff0c;直接搜索obsidian 插件名&#xff0c;obsidianSyntax Highl…

Halcon 图片分割 米粒分水岭(高斯滤波,区域距离计算,分水岭处理)

资源&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1kmxdMk67E-7QCsG5mKnc7A 提取码&#xff1a;555s 图片 代码 * 1.读取并显示图片 ************************************* dev_close_window () read_image (Image, ./img.png) get_image_size (Image, Width, He…

JVM垃圾回收

JVM 快速开始&#xff1a; 请谈谈你对JVM 的理解&#xff1f;java8 的虚拟机有什么更新&#xff1f; 什么是OOM &#xff1f;什么是StackOverflowError&#xff1f;有哪些方法分析&#xff1f; JVM 的常用参数调优你知道哪些&#xff1f; 内存快照抓取和MAT分析DUMP文件知道…

Android databinding的接入使用与详解(一)

一、介绍 DataBinding 是Google Android组件框架&#xff0c;管理view和data之间进行绑定。DataBinding主要管理数个布局文件&#xff0c;这样我们就不用去实例化layout的view。直接通过DataBindingUitl来完成初始化。 这样可以精简代码&#xff0c;也减少工作量&#xff0c;避…

2022双十二有哪些值得入手的数码好物?值得入手的数码好物推荐

双十二快到了&#xff0c;不少人都会选择在这个时候入手数码产品&#xff0c;但又不知道有哪些值得入手。下面&#xff0c;我来给大家推荐几款实用性高&#xff0c;入手性强的数码好物&#xff0c;感兴趣的一起来看看吧。 一、南卡小音舱蓝牙耳机 推荐理由&#xff1a;蓝牙5.…

带你初识JSP(JAVA服务器页面)

文章目录前言第一个 JSP 程序什么是Java Server Pages?为什么使用JSP&#xff1f;JSP的优势配置Java开发工具&#xff08;JDK&#xff09;设置Web服务器&#xff1a;Tomcat设置 CLASSPATH 环境变量JSP 结构JSP 处理JSP 生命周期JSP编译JSP初始化JSP执行JSP清理前言 JSP 与 PH…

ABAP CLEAR REFRESH FREE 说明(刘欣)

本文仔细测试总结了ABAP中的clear、refresh、free&#xff0c;因为很多时候程序的BUG就是出现在变量没有清理干净&#xff0c;希望整理一个定式出来以后少出BUG。 用clear、refresh、free对带表头的表执行的测试结果如下表&#xff1a; 看起来&#xff0c;最好的避免这些清空命…

Oracle11g安装

参考教程 Oracle11g安装配置详细教程 oracle11g安装步骤详细图文教程 但是这里的用户名如果是以system的话&#xff0c;密码错误 Oracle默认账号密码&#xff1a; &#xff08;1&#xff09;普通用户&#xff1a; SCOTT &#xff08;密码&#xff1a;tiger&#xff09; &…

Day17--购物车页面-商品列表-封装NumberBox

提纲挈领&#xff1a; 官方文档提供了uni-number-box组件 文档内容&#xff1a; 我的操作&#xff1a; 1》修改 my-goods.vue 组件的源代码&#xff0c;在类名为 goods-info-box 的 view 组件内部渲染 NumberBox 组件的基本结构&#xff1a; 2》美化其样式 *****************…

基于最小均方误差linear minimum mean square error(LMMSE)插值算法的图像超分辨重构研究-附Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、图像复原基本原理✳️ 三、基于多通道LMMSE图像复原法✳️ 3.1 最小均方误差LMMSE插值理论✳️ 3.2 理论公式对应的Matlab关键代码✳️ 四、实验验证✳️ 五、参考文献✳️ 六、Matlab程序获取与验证✳️ 一、引言 图像是一种表达信息的…

进程与线程的相爱相杀

✨✨hello&#xff0c;愿意点进来的小伙伴们&#xff0c;你们好呐&#xff01; &#x1f43b;&#x1f43b;系列专栏&#xff1a;【JavaEE初阶】 &#x1f432;&#x1f432;本篇内容&#xff1a;详解进程与线程 &#x1f42f;&#x1f42f;作者简介:一名现大二的三非编程小白&…

Linux文件系统

Linux文件系统 文章目录Linux文件系统1.对文件系统的理解1.1 文件系统当中的缓冲区1.2 文件系统当中的inode1.3 文件属性与文件数据分开存放原理2.对软硬链接的理解扩展&#xff1a;对文件三时间的理解1.对文件系统的理解 1.1 文件系统当中的缓冲区 我们来看看下面这段代码&a…

基于K8s构建Jenkins持续集成平台(部署流程)(转了一点)

转载至&#xff1a;https://blog.csdn.net/m0_59430185/article/details/123394853 一、传统Jenkins的Master-Slave方案的缺陷 Master节点发生单点故障时&#xff0c;整个流程都不可用了 每个 Slave节点的配置环境不一样&#xff0c;来完成不同语言的编译打包等操作&#xff0…

【Uni-App】点击分享,生成海报带二维码,保存到本地图片

目录一&#xff1a;需求二&#xff1a;分析三&#xff1a;准备工作1.qrcode准备2.并且在main.js去挂载四&#xff1a;页面构建1.html2.data3.js一&#xff1a;需求 1.产品需要这个商品&#xff0c;必须分享才有购买资格 2.一共三点&#xff0c;有海报&#xff0c;有二维码&…

k8s安装3节点集群Fate v1.8.0

集群配置信息 3节点配置信息如下图&#xff1a; 当kubefate最新版是1.9.0时&#xff0c;依赖的k8s和ingress-ngnix版本如下&#xff1a; Recommended version of dependent software: Kubernetes: v1.23.5 Ingress-nginx: v1.1.3 升级K8S到1.23.5 参考博客 https://blog.c…