[STL]vector的使用+模拟实现

news2024/10/3 2:25:48

[STL]vector的使用+模拟实现

文章目录

      • [STL]vector的使用+模拟实现
        • 一、vector的使用
          • 1.构造函数
          • 2.迭代器
          • 3.容量操作
          • 4.vector的访问
          • 5.vector的修改
        • 二、几个细节
          • 1.范围for
          • 2.扩容机制
          • 3.迭代器失效
          • 4.构造函数错误调用
          • 5.vector的深拷贝与浅拷贝
          • 6.vector的框架
        • 三、vector模拟实现
          • vector.h
          • test.cpp

一、vector的使用

1.构造函数

在这里插入图片描述

vector的构造函数提供了4种构造方式

1.无参构造方式

2.n个val的构造方式

3.迭代器区间的构造方式

4.拷贝构造

其中最后的参数是一个空间配置器(提高空间分配效率),一般我们默认不用传这个参数。

在这里插入图片描述

其中,在n个val的构造中,默认的缺省值是T(),而不是0。这是因为在T不仅仅是内置类型,也可以是自定义类型。例如:string、stack、queue等。

我们由此发现在C++中,内置类型也可以使用匿名对象。例如:int()、double()、char()等。

2.迭代器

在这里插入图片描述

和string中说的很相似。


  • 普通迭代器

  • begin : 返回容器中的第一个位置

  • end : 返回容器中的最后一个位置


  • 反向迭代器

  • rbegin : 返回容器中的最后一个位置

  • rend : 返回容器中的第一个位置


  • const对象迭代器
  • cbegin : 返回容器中的第一个位置
  • cend :返回容器中的最后一个位置

  • const对象反向迭代器
  • crbegin : 返回容器中的最后一个位置
  • crend : 返回容器中的第一个位置
3.容量操作

在这里插入图片描述

  • size : 返回容器中的内容大小

  • max_size : 返回容器最大容量大小

  • capacity : 返回容器中的容量

  • empty : 返回容器是否为空

在这里插入图片描述

  • resize : 改变容器中空间大小
    在这里插入图片描述
  1. 如果n < v.size(),缩写原来的大小到n
  2. 如果 v.size() < n < v.capcaity()修改容器size为n。
  3. 如果 n > v.capacity(), 容器扩容到n

在这里插入图片描述

  • reserve : 改变容器中的容量大小

1.n < v.capacity(), 不变

2.n > v.capacity(), 改变容器容量

在这里插入图片描述

4.vector的访问

在这里插入图片描述

  • operator[] : 容器的随机访问
  • front : 容器的第一个位置的内容
  • back : 容器的最后一个位置的内容

在这里插入图片描述

5.vector的修改

在这里插入图片描述

  • push_back : 尾部插入
  • pop_back : 尾部删除

在这里插入图片描述

  • insert : 插入

在这里插入图片描述

1.pos位置 + val值

2.pos位置 + n个val值

3.pos位置 + 迭代器区间
在这里插入图片描述

  • erase : 删除

在这里插入图片描述

  1. pos位置删除
  2. 迭代器区间删除

在这里插入图片描述

  • swap : 交换
  • clear : 清空容器

在这里插入图片描述

二、几个细节

1.范围for

范围for的原理十分简单,仅仅是对代码进行了替换。通过调用begin()和end()接口进行比对,判断循环是否停止。

我们可以进入反汇编中去查看汇编下范围for是怎么生成的。

在这里插入图片描述

如果我们在模拟实现中,将begin()和end()改为Begin()和End(),那样编译器无法识别就会报错,所以在使用范围for时,一定要确保begin()和end()是否正确。

2.扩容机制

我们可以用下面这么一段代码测试一下编译器的扩容机制:(我使用的是vs2019)

int main()
{
	vector<int> v;
	size_t capacity = 0;
	capacity = v.capacity();
	cout << " vector change : " << endl;
	for (int i = 0; i < 100; i++)
	{
		v.push_back(i);
		if (capacity != v.capacity())
		{
			capacity = v.capacity();
			cout << " vector capacity : " << capacity << endl;
		}
	}
	return 0;
}

在这里插入图片描述

我们发现vs2019的扩容机制是约1.5倍的扩容。

我们再看一下linux下的扩容机制。

在这里插入图片描述

我们看到linux下扩容是一个标准二倍的扩容。

3.迭代器失效

我们使用insert和erase一般配合着算法库里的find接口使用。

  • find : 查找数据,有,返回pos位置, 无,
    在这里插入图片描述
    在这里插入图片描述

我们去掉解引用pos:

在这里插入图片描述

这里就是我们所说的迭代器失效问题:

在我们使用insert和erase后,迭代器pos就会失效,需要使用就得更新一下pos :

在这里插入图片描述

所以一般我们实现的和STL库中的,都会有一个有返回值的insert和erase接口。

但是linux下不会出现这种情况。
在这里插入图片描述

在这里插入图片描述

原因可能是vs对库进行了封装,我们每次使用insert和erase后,vs都进行了处理。而linux下使用的是原生的,没有进行过处理或者修改。

所以,我们默认使用insert和erase后迭代器会失效,需要我们进行更新后使用。

4.构造函数错误调用
    vector(size_t n, const T& val = T())
    	:_start(nullptr)
    	, _finish(nullptr)
    	, _end_of_storage(nullptr)
    {
    	reserve(n);
    	for (size_t i = 0; i < n; i++)
   	 	{
   			 push_back(val);
    	}
    }

    template <class InputIterator>
    vector(InputIterator first, InputIterator last)
    	:_start(nullptr)
    	, _finish(nullptr)
    	, _end_of_storage(nullptr)
   		 {
   			 while (first != last)
    			{
    				push_back(*first);
    				++first;
    			}
    	}

在这里插入图片描述

在我们构造一个有n个val的vector时(仅仅在构造int类型时出现),会出现一个奇怪的报错非法访问寻址

在这里插入图片描述

这里报错的原因是,编译器分不清我们调用的是哪个构造函数,编译器为了方便简单,选择了将两个Inputlterator实例化为int,而不是一个隐式转换为size_t,一个实例化为int。这样编译器就会调用迭代器构造,而迭代器构造中就会有解引用,对一个整形解引用就会出现上面的非法访问寻址。

而解决的方法很简单,我们手动写一个int类型的构造即可。

vector(int n, const T& val = T())
		:_start(nullptr)
		, _finish(nullptr)
		, _end_of_storage(nullptr)
	{
		reserve(n);
		for (int i = 0; i < n; i++)
		{
			push_back(val);
		}
	}

在这里插入图片描述

5.vector的深拷贝与浅拷贝

我们在实现reserve接口时,不进行开辟新的空间就会出现浅拷贝问题。

对于浅拷贝问题,大体上都是这样的:

在这里插入图片描述

我们将新开辟的空间中记录了被释放掉的旧空间,当程序结束又对这块空间进行了释放。

我们这里的reserve也容易出现这里的浅拷贝问题(只会对使用自定义类型出现):

void reserve(size_t n)
{
	if (n > capacity())
	{
	size_t OldSize = size();
	T* tmp = new T[n];

	if (_start)
	{
	memcpy(tmp, _start, sizeof(T) * size());
	delete[]_start;
	}
	_start = tmp;
	_finish = tmp + OldSize;
	_end_of_storage = _start + n;
	}
}

我们没有开辟储存数据的空间,而是直接使用memcpy按字节拷贝过来,虽然我们对存放v[i]进行了深拷贝,但是他们可能还是指向了原来的将要被析构的旧空间。当旧空间被析构,而程序结束时这块空间再次被析构。造成了对同一块空间进行多次析构的错误。

修改方法很简单:

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t OldSize = size();
		T* tmp = new T[n];

		if (_start)
		{
			//memcpy(tmp, _start, sizeof(T) * size());
			for (size_t i = 0; i < OldSize; i++)
			{
				tmp[i] = _start[i];
			}
			delete[]_start;
		}
		_start = tmp;
		_finish = tmp + OldSize;
		_end_of_storage = _start + n;
	}
}
6.vector的框架

在这里插入图片描述

通过读《STL的源码剖析》,我发现了一个vecor的框架,有助于我们对vector有更深的理解。

三、vector模拟实现

vector.h
#pragma once
#include <string.h>
namespace myVector
{
	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;
		}

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

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

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

		vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		vector(size_t n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

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

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

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

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t OldSize = size();
				T* tmp = new T[n];

				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * size());
					for (size_t i = 0; i < OldSize; i++)
					{
						tmp[i] = _start[i];
					}
					delete[]_start;
				}
				_start = tmp;
				_finish = tmp + OldSize;
				_end_of_storage = _start + n;
			}
		}

		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;
			}
		}

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

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

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

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

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

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			//判断扩容
			if (_end_of_storage == _finish)
			{
				size_t len = _finish - _start;
				size_t NewCapcity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(NewCapcity);
				pos = _start + len;
			}
			//挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				(*end + 1) = (*end);
				--end;
			}
			*pos = val;
			++_finish;

			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *begin;
				++begin;
			}
			--_finish;

			return pos;
		}

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

		void clear()
		{
			_finish = _start;
		}


	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
#include "vector.h"
void test()
{
	myVector::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.begin();
	v.pop_back();
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
	cout << v.size() << endl;
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i];
		cout << ' ';
	}
	cout << endl;
}

void test1()
{
	myVector::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
	cout << v.size() << endl;
	v.reserve(10);
	cout << v.capacity() << endl;
	cout << v.size() << endl;
}
void test2()
{
	myVector::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
	cout << v.size() << endl;
	v.reserve(11);
	cout << v.capacity() << endl;
	cout << v.size() << endl;
	v.resize(9, 2);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << v.capacity() << endl;
	cout << v.size() << endl;
}
void test3()
{
	myVector::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	myVector::vector<string> v1;
	v1.push_back("111");
	v1.push_back("111");
	v1.push_back("111");
	v1.push_back("111");
	v1.push_back("111");

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}
void test4()
{
	myVector::vector<myVector::vector<int>> vv;
	myVector::vector<int> v(5, 1);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);

	for (size_t i = 0; i < vv.size(); ++i)
	{
		for (size_t j = 0; j < vv[i].size(); ++j)
		{
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

void test5()
{

		// 要求删除所有偶数
		myVector::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		//v.push_back(5);


		myVector::vector<int>::iterator it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it = v.erase(it);
			}
			else
			{
				++it;
			}
		}

		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	

}

void test6()
{
	// 要求删除所有偶数
	myVector::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	myVector::vector<int> v1(v);

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	myVector::vector<int> v2;
	v2.push_back(10);
	v2.push_back(20);
	v1 = v2;
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1 = v1;
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test7()
{
	std::string str("hello");

	myVector::vector<int> v(str.begin(), str.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//vector<int> v1(v.begin(), v.end());
	myVector::vector<int> v1(10, 1);
	//vector<char> v1(10, 'A');

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test8()
{
	myVector::vector<myVector::vector<int>> vv;
	myVector::vector<int> v(5, 1);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);
	vv.push_back(v);

	for (size_t i = 0; i < vv.size(); ++i)
	{
		for (size_t j = 0; j < vv[i].size(); ++j)
		{
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

class Solution {
public:
	myVector::vector<myVector::vector<int>> generate(int numRows) {
		myVector::vector<myVector::vector<int>> vv;
		vv.resize(numRows);
		for (size_t i = 0; i < vv.size(); ++i)
		{
			vv[i].resize(i + 1, 0);
			vv[i][0] = vv[i][vv[i].size() - 1] = 1;
		}

		for (size_t i = 0; i < vv.size(); ++i)
		{
			for (size_t j = 0; j < vv[i].size(); ++j)
			{
				if (vv[i][j] == 0)
				{
					vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
				}
			}
		}

		return vv;
	}
};

void test9()
{
	myVector::vector<myVector::vector<int>> vvRet = Solution().generate(5);

		for (size_t i = 0; i < vvRet.size(); ++i)
		{
			for (size_t j = 0; j < vvRet[i].size(); ++j)
			{
				cout << vvRet[i][j] << " ";
			}
			cout << endl;
		}
		cout << endl;
}

int main()
{
	//test();
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	//test6();
	//test8();
	test9();
	return 0;
}

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

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

相关文章

hitcon_2017_ssrfme、[BJDCTF2020]Easy MD5、[极客大挑战 2019]BuyFlag

hitcon_2017_ssrfme 进入环境给出源码 <?php if (isset($_SERVER[HTTP_X_FORWARDED_FOR])) {$http_x_headers explode(,, $_SERVER[HTTP_X_FORWARDED_FOR]);$_SERVER[REMOTE_ADDR] $http_x_headers[0];}echo $_SERVER["REMOTE_ADDR"];$sandbox "sandbo…

Leetcode225. 用队列实现栈

文章目录 1.题目描述2.原题链接3.思路分析4.代码实现 1.题目描述 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int…

树上差分(点差分/边差分)

树上差分一般有两种类型的题目&#xff0c;一种是对边进行差分&#xff0c;另一种就是对点进行差分。 对应的操作也有两种&#xff0c;对边进行差分的对应操作就是给定一对节点(u,v)&#xff0c;让我们把u到v之间路径上的边权都加val&#xff0c;对点进行差分的对应操作就是给…

经验正交分解EOF的Matlab的实现示例

在地学中&#xff0c;PCA和EOF通常用于信号提取&#xff0c;从繁杂的时空数据中分离出地理要素的时空变化特征&#xff0c;是进行地学信号分析的前提。本质上PCA和EOF没有什么不同&#xff0c;只是&#xff1a;EOF为空间特征向量&#xff0c;也称为空间模态&#xff0c;在一定程…

信号完整性分析:关于传输线的三十个问题解答(一)

1.什么是真正的传输线&#xff1f;&#xff08;What is a real transmission line?&#xff09; 答&#xff1a;真正的传输线由任意两条延长一定长度的导体组成。将一根导线标记为信号路径&#xff0c;将另一根导线标记为返回路径。 A real transmission line is composed o…

2023最经典的Python接口自动化测试中的用例编写问题总结

本篇文章分享几个接口自动化用例编写过程遇到的问题总结&#xff0c;希望能对初次探索接口自动化测试的小伙伴们解决问题上提供一小部分思路。 B站讲的最详细的Python接口自动化测试实战教程全集&#xff08;实战最新版&#xff09;_哔哩哔哩_bilibiliB站讲的最详细的Python接…

4月,不要跳槽...

跳槽是每个人都可能面临的选择&#xff0c;但不同的时间点会对跳槽带来不同的影响。对于软件测试人员来说&#xff0c;4月份并不是最适合的跳槽时间。原因如下&#xff1a; 与企业目标和计划相关。一般情况下&#xff0c;公司在1月份会制定本年度的发展目标和计划&#xff0c;而…

力扣sql中等篇练习(五)

力扣sql中等篇练习(五) 1 股票的资本收益 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 每个用户的所有Sell的price值减去Buy的price值就可以了 SELECT stock_name,SUM(IF(operationBuy,price*-1,price)) capital_gain_loss FROM Stocks GROUP B…

IT知识百科:什么是SSID?

一、什么是SSID SSID&#xff08;Service Set Identifier&#xff09;是无线网络中的一个重要概念&#xff0c;它是一个用于标识无线局域网&#xff08;WLAN&#xff09;的名称。SSID可以看作是无线网络的名称&#xff0c;类似于有线网络中的网络名称或者路由器的名称。在无线…

【JavaScript】5.JavaScript内置对象

JavaScript 内置对象 JavaScript 中的对象分为3种 自定义对象内置对象浏览器对象 前面两种对象是JS 基础 内容&#xff0c;属于 ECMAScript&#xff1b; 第三个浏览器对象属于JS 独有的 内置对象就是指 JS 语言自带的一些对象&#xff0c;这些对象供开发者使用&#xff0c;…

数据通信基础 - 数据通信方式

文章目录 1 概述2 分类2.1 按通信方向分2.2 按同步方式分 3 扩展3.1 网工软考真题 1 概述 分类维度分类解释举例通信方向单工通信信息 只能在一个方向发送&#xff0c;发送方不能接收&#xff0c;接收方不能发送电视、广播半双工通信通信双方可以 交替发送和接收信息&#xff…

分布式锁+AOP实现缓存

分布式锁AOP实现缓存 1、分布式锁AOP实现思想2、不使用AOP的情况2.1 没有使用缓存时代码2.2 使用Redis实现分布式锁的代码2.3 使用Redisson实现分布式锁2.4 测试缓存命中2.5 存在问题 3、分布式锁AOP实现3.1 定义注解3.2 定义一个切面类加上注解3.3 使用注解完成缓存 1、分布式…

函数的缺省参数,函数重载与底层函数名修饰解释,引用的初步介绍

TIPS 使用C输入输出更方便&#xff0c;不需要像printf/scanf输入输出时那样&#xff0c;需要手动控制格式。C的输入输出可以自动识别变量类型。在日常练习中&#xff0c;建议直接using namespace std即可&#xff0c;这样就很方便。using namespace std展开&#xff0c;标准库…

ReetrantLock源码剖析_03公平锁、非公平锁

一直努力就会有offer&#xff0c;一直努力就会有offer&#xff0c;一直努力就会有offer&#xff01; 文章目录 ReetrantLock公平锁代码解析ReetrantLock公平锁执行流程ReetrantLock非公平锁代码解析ReetrantLock非公平锁执行流程公平锁与非公平锁的比较 ReetrantLock公平锁代码…

前端部署发布项目后,如何通知用户刷新页面、清除缓存

以下只是一些思路&#xff0c;有更好的实现方式可以留言一起交流学习 方式一&#xff1a;纯前端 在每次发布前端时&#xff0c;使用webpack构建命令生成一个json文件&#xff0c;json中写个随机生成的一个字符串&#xff08;比如时间戳&#xff09;&#xff0c;每次打包程序都…

【Python入门第五十天】Python丨NumPy 数组搜索

搜索数组 可以在数组中搜索&#xff08;检索&#xff09;某个值&#xff0c;然后返回获得匹配的索引。 要搜索数组&#xff0c;请使用 where() 方法。 实例 查找值为 4 的索引&#xff1a; import numpy as nparr np.array([1, 2, 3, 4, 5, 4, 4])x np.where(arr 4)pri…

node可以用nvm快速切换版本,golang如何快速切换版本?用gvm就行。

使用 gvm 可以带来以下好处&#xff1a; 快速切换 Golang 版本&#xff0c;方便进行版本测试和开发&#xff1b;可以在多个项目中同时使用不同版本的 Golang 包和工具&#xff0c;避免冲突&#xff1b;可以通过 gvm 管理不同版本的 Golang&#xff0c;方便安装、卸载和更新&am…

STL--vector

一、vector介绍 vector是表示大小可以更改的数组的序列容器 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而…

移动端屏幕适配

文章目录 移动端屏幕适配移动端屏幕适配和响应式布局区别基本知识简单屏幕适配 移动端屏幕适配 移动端屏幕适配和响应式布局区别 移动端适配响应式布局终端移动端PC端和移动端常用单位宽高&#xff1a;rem 或 %字体&#xff1a;px宽&#xff1a;%高、字体&#xff1a;px宽高宽…

Docker网络模式与cgroups资源控制

目录 1.docker网络模式原理 2.端口映射 3.Docker网络模式&#xff08;41种&#xff09; 1.查看docker网络列表 2.网络模式详解 4.Docker cgroups资源控制 1.CPU资源控制 2.对内存使用的限制 3.对磁盘IO的配置控制&#xff08;blkio&#xff09;的限制 4.清除docker占用…