【C++】手把手教你模拟实现vector

news2024/9/29 11:22:27

vector模拟实现

    • 前言
    • 正式开始
      • 三个成员变量
      • 无参构造
      • 析构
      • push_back
      • [ ]重载
      • pop_back
      • insert
      • erase
      • 迭代器失效问题
        • insert迭代器失效
        • erase迭代器失效
      • 深浅拷贝
        • 拷贝构造函数
        • 赋值运算符重载
      • n个val构造
      • resize
      • front和back
        • front
        • back

在这里插入图片描述

前言

这篇写的是vector的模拟实现。

如果对于vector不熟悉的话,可以看前一篇博客:vector基本用法介绍

本篇不会讲太多实现上的细节,会偏重迭代器方面的讲解,如果想要更好理解构造函数、[]运算符重载等函数的实现的话,可以看这篇string的模拟实现,因为string的用法和vector是差不多的:手把手教你模拟实现string类

正式开始

模拟实现,首先要搞清楚其主要的成员变量有啥。

其实就3个:start、finish、end_of_storage。
这三个都是指针,start指向所开辟的空间的首地址,finish指向实际存储元素的下一个位置,end_of_storage指向开辟空间的末尾的下一个位置。
在这里插入图片描述

前一篇介绍vector的博客也说了,vector是一个模板类。
模板参数T,还有一个内存池。
但是这里实现的话,就不搞内存池了,先把一些基本的搞懂,再说那内存池的东西,后面自然会说的。

然后库中的实现,有type_name什么我们未接触到STL时不懂的类型,但是上一篇也说了,这篇就讲了,直接用,但是把库中的实现拿出来看一下:
在这里插入图片描述

库中对于T*,搞了两个类型,一个是pointer,一个时iterator。

我们就用后面那个就行。这一个iterator就是一个T而已,前面也是说过了, vector和string这种空间是连续的数据结构,其迭代器底层就是原生的指针。我们用iterator就用的是T,如果T是int的话,就是int*。

废话不多说,开搞。

三个成员变量

先把迭代器搞出来,非常简单,不要觉得听起来很高大上,就长这样:
在这里插入图片描述
然后在搞三个成员变量:
在这里插入图片描述
前面那张图也提到了,这里再放出来理解一下:
在这里插入图片描述
然后就是构造。

无参构造

先看一下库中是怎么实现的:
在这里插入图片描述
给的是0,就是空指针nullptr。

我们也写一下:
在这里插入图片描述

不多赘述,接着看析构:

析构

因为三个成员变量都是指针,且指向同一块空间,所以释放最前面的那个指针就行了。
在这里插入图片描述
想要简单使用的话,在搞一下尾插尾删就可以了。

push_back

尾插的话,是增加元素,只要增加,第一时间想到的就是扩容。
扩容的话,我们需要知道size和capacity的确切值。
也很简单,指针减去指针就可以了:
size: _finish - _start 就是size。
capacity: _end_of_storage - _start 就是capacity。

然后只要size和capacity相等的时候就要扩容,初始情况下,二者都为nullptr也成立。

然后光是上面的这些话,就要写三个函数:size()、capacity()、reserve()。
分别是size、capacity和扩容。

代码具体怎么实现的就不说了,前面C语言写顺序表的时候已经说过了,这里就不把时间放在这种基本功上面的了。

先实现一下:
在这里插入图片描述
其实上面的扩容是有bug的,不知道细心的同学发现了没有。

当我们扩完了容之后,如果_start不是是nullptr就需要将原来的_start中的数据转到tmp中,再改变_finish,但是改finish的时候调用size(),进入到size函数中_finish - _start,此时_start已经变了,但是finish并没有变,所以把size()替换为_finish - _start后,整个式子就变成了_finish = _statr + _finish - _start = _finish。故当我们扩了容之后finish并没有改变,从头到尾一直是nullptr。所以就出问题了。

我们把push_back写完后来试试:
在这里插入图片描述

在这里插入图片描述
可以看到,出了问题,就是finish一直是那个nullptr,变不了,那么怎么改呢?

两种方法:

  1. 将_start的赋值语句和_finish的赋值语句调换下位置,并将_finish赋值语句中的_start变为tmp。
    在这里插入图片描述
    此时再调试,就可以了:
    在这里插入图片描述

  2. 在前面将未改变的size记录下来,并将_finish赋值语句中的size()改变为记录下来的原始size。
    在这里插入图片描述

只要在_start改变之前记录下来就可以了。

再调试,也可以:
在这里插入图片描述

更推荐第二种写法。

[ ]重载

再实现下[]操作符重载。
前面库中vector的成员类型有个reference,就是引用,我们也写一个。用来实现[]重载。
在这里插入图片描述
用一下:
在这里插入图片描述
但是如果是const对象的话,就不能用了。
所以也要写一个const的[]重载:
在这里插入图片描述

想要遍历顺序表中的元素的话,前一篇也说了,三种方法。

  1. 前面的for循环
  2. 迭代器
    迭代器前面也定义了。就是T*。
    然后搞一下begin和end:
    在这里插入图片描述
    很简单,就是原生指针。没有太大的含金量。

然后我们用这个遍历试一下:
在这里插入图片描述
和库中的用法是一样的。

  1. 范围for
    有了迭代器就可以用范围for了,因为范围for底层就是迭代器的“傻瓜式”替换。

看:
在这里插入图片描述
但是如果我们把begin和end函数名改一下,范围for就用不了了。

比如说第一个字母给大写:
在这里插入图片描述

此时范围for报错:
在这里插入图片描述
所以说范围for底层就是迭代器的傻瓜式的替换。只认识begin和end,字母变一下就不认识了。

然后库中也有const对象对应的迭代器,这里也实现一下:
在这里插入图片描述

在这里插入图片描述
调用一下:
在这里插入图片描述
在这里插入图片描述

这里const对象也是变一下begin和end就会范围for就会失效。
正常情况下:
在这里插入图片描述
变了begin/end后:
在这里插入图片描述
又不能用了。

这个讲到这。
下面说pop_back。

pop_back

很简单,还是要先考虑是否为空,然后_finish减一下就行。

在这里插入图片描述

在这里插入图片描述

然后说insert和erase。

insert

很简单,string中也是讲过的,不说细节了,直接给代码:
在这里插入图片描述

这里的insert要配合着find来使用。
在这里插入图片描述

erase

库中的erase两个函数返回值都是Iterator,但是insert有的是返回Iterator,有的是void,所以说这里我就只实现了一个返回Iterator。

也是直接给实现:
在这里插入图片描述
再调用一下:
在这里插入图片描述

但是上面的有些要注意的地方。
就是迭代器失效。

迭代器失效问题

insert迭代器失效

先说insert的迭代器失效问题:
看代码:
在这里插入图片描述
这里直接崩掉了。
原因很简单,就是我们在pos位置插入时,pos位置已经失效了。
为什么pos位置会失效呢?
注意看,顺序表中本来就只有1、2、3、4四个元素,所以说当我们插入数据时会扩容,如果扩容,那么原空间地址就会失效,换成新的地址,此时pos位置仍然指的是原空间的地址,所以说,扩完容后再插入,就会非法访问源地址空间,此时就会出错,这就是所谓的迭代器失效。

那么怎么避免呢?
也很简单,扩了容之后将pos位置进行修正就可以了。
看:
在这里插入图片描述

再测试:
在这里插入图片描述
此时就没事了,但只是这里没事了,我们再来搞一个测试。

pos位置处插入数据后,再到pos位置处插入数据:
在这里插入图片描述
断言崩掉了。

这里还是因为pos,我们是传值传的pos,insert函数中的pos改了,但是外面的pos不会改变,第一次传过去,pos还是在那个合法范围中(start ~ finish)的,第二次传过去pos还是原来的那个pos,就完全不在合法范围了。

有的同学可能就说为什么不将参数改为引用呢?
那么我只能说,当我传参是begin()或者end()时,你又该如何应对?

传参为普通的指针时,可以通过:
在这里插入图片描述

如果改为引用:

虽然pos可以传:
在这里插入图片描述

但是如果我传begin的话:

在这里插入图片描述
就传不过去了。

因为begin返回时不是返回_start本身,而是返回一个临时数据,临时数据具有常属性,不能被修改,如果传给引用成功的话,就会导致权限被放大,导致临时数据能被修改,所以就不能传引用了。

那又有同学说能不能将参数改为加const的呢?
还是不能,因为加了const后,你insert里面的那个pos怎么修改呢?
所以说是不行的。

库中的实现,参数也是Iterator就完了,没有加引用或者const。
在这里插入图片描述
说一点最重要的:当使用pos插入了元素之后,就尽量不要再使用pos访问那个位置了。

我上面模拟实现的insert是有点小瑕疵的。返回值要改为返回一下插入位置的迭代器。
代码如下:
在这里插入图片描述

那么insert就讲到这,下面说erase的迭代器失效。

erase迭代器失效

仔细看我的erase模拟实现。是不会出现迭代器失效的问题的。
但是不能说库中的erase不会失效。

这里的模拟实现并没有进行缩容的操作,缩容就是指当实际元素个数小于容量的
一半或者多少倍时,就将顺序表的容量进行缩减,一般不会这样做,但是STL并没有规定说不能这样做,可能有的库中会这样进行实现,如果进行缩容了的话,原空间丢失,就又会导致insert中的迭代器pos位置失效。

这里就不演示缩容的了,这种情况很少见,大家只要懂了上面insert中的迭代器失效,相信这里也是能懂erase失效的。

那就展示个别的,删除所有偶数:
给出如下代码:

auto it = v1.begin();
// 删除所有偶数
while (it != v1.end())
{
	if (*it % 2 == 0)
	{
		v1.erase(it);
	}

	++it;
}

上面的代码中给出v1三种情况:

  1. 1、2、3、4、5
  2. 1、2、3、4
  3. 1、2、4、3、4、5

三种情况,结果各不相同。
因为上面的代码有问题。

先把挨个的结果给出来:

第一种:结果正确。
在这里插入图片描述

第二种:程序崩溃。
在这里插入图片描述

第三种:结果错误。
在这里插入图片描述

为什么?
我只能说大家画一下图,自己模拟一下删除的过程。
我这里也不好演示,能力有限,不会搞那种动图。

第一种情况是凑巧,歪打正着。
第二种情况是越界,程序崩掉了。
第三种情况是,将偶数直接跳过了。

那么把上面的代码改改就行。

库中的erase是有返回值的,都是返回删除位置的下一个数据的迭代器的位置。

那么就好说了,我们每次删除一个数据之后就更新一下当前迭代器的位置就解决了。

代码如下:

auto it = v1.begin();
while (it != v1.end())
{
	if (*it % 2 == 0)
	{
		it = v1.erase(it);
	}
	else
	{
		++it;
	}
}

这样就好了。
上面的三个场景都是正确的。

总结一下:
insert / erase pos位置,不要直接访问,一定要更新,直接访问可能出现各种各样的结果,就是所谓的迭代器失效。

STL只是一个规范,只规定了要实现什么东西,具体的实现细节没有做什么要求,不同的平台下的实现是不同的。
上面的三个示例,g++和vs2019下结果一样,但是vs2013下三种情况都会报错。就是因为库的实现不同。

下面说一说深浅拷贝的问题。

深浅拷贝

深浅拷贝对于自定义类型来说已经是家常便饭了。
老问题。浅拷贝的结果是:1. 析构两次, 2. 一个对象修改同时影响多个对象。

深浅拷贝存在于拷贝构造函数和赋值运算符重载中。挨个说。

拷贝构造函数

这里给三种实现方法。

  1. 正常实现

直接给代码:
在这里插入图片描述
注意上面const对象不能调用非const成员函数,所以还要再写一个const的size()。

测试一下:
在这里插入图片描述

  1. 复用reserve和push_back
    在这里插入图片描述
    测试一下:
    在这里插入图片描述

但其实,代码是稍微有点问题的。
vs2019会自动初始化成员变量,如果别的平台没有初始化的话,就会有问题。
没有初始化的话,_start就会变为野指针,当reserve的时候就会先拷贝脏数据然后再释放未开辟的空间,就会导致程序崩溃。
所以说得加上初始化:
在这里插入图片描述
这样才比较完善。

  1. 复用构造再交换
    我前面在模拟实现string那篇中也用到了这个方法,但是string有个构造函数是可以多个元素初始化成不同数据的,但vector中只有个迭代器区间初始化时可以实现这样的初始化,所以我们要先实现以下这个迭代器区间的构造函数。

迭代器区间我们可以直接用我们自己写的那个iterator,但是也可以用一个函数模板来实现,后者的好处在于能够用不同类的迭代器区间来构造类对象。可能你没有听懂,没关系,等会给示例就懂了。
像下面这样的迭代器区间:
在这里插入图片描述

实现出来就是这样:
在这里插入图片描述

测试一下:
在这里插入图片描述

也是有点问题,没有初始化。但是vs2019会默认初始化。
加上:
在这里插入图片描述

然后我们试一下刚才说的不同类型初始化。
在这里插入图片描述
就是上面的这个。

然后就是拷贝构造函数:
在这里插入图片描述

上面拷贝构造函数中swap没有用库中的,因为直接用库中的话是深拷贝开销会很大,没必要,自己实现一个就好。

测试一下:
在这里插入图片描述
再来说一下赋值运算符重载。

赋值运算符重载

还是先给出普通的实现:

在这里插入图片描述

测试下:
在这里插入图片描述

再用一个比较简便的方式:
在这里插入图片描述
测试一下:
在这里插入图片描述

可能基础不牢的同学这个方法已经看懵了,不要懵,听我讲。

v3 = v2。v2以传值的形式传给了v,所以说v是v2的一份拷贝,将这个拷贝的数据与v3进行交换,就能把v中与v2相同的值全部交换给v3,这样就能达成赋值的目的,然后重载函数运行完毕,栈帧销毁,v被释放,不会造成任何影响还能给v3赋值。

然后再来看个构造函数。

n个val构造

直接给代码:
在这里插入图片描述

测试一下:
在这里插入图片描述
细心的同学可能已经发现了v1初始化的时候用的是8u,也就是无符号整型8。
但是我为什么要这么做呢?

看一下我不加u的结果:
在这里插入图片描述
出错了,编译器说我非法的间接寻址。

为什么?
直接说,就是因为前面我写的那个迭代器的构造。
这里参数是两个int,也就是(int, int)。
编译器在匹配重载的函数的时候,是按照最匹配的函数来进行匹配的。
我们的迭代器区间构造函数,参数是(InputIterator, InputIterator)这两个一样的参数,也就是说,我们没有参数为 (int, int) 的构造函数,所以说当我们传参为int,int最匹配的就是(InputIterator, InputIterator),而不是(size_t n, const T& val = T()),所以此时调用的就是迭代器区间的构造函数。

那么如何避免这个问题呢?
我看库里面的实现是再专门搞一个参数为(int, const T&)的,甚至还有个(long, const T&):
在这里插入图片描述
所以我们模拟的话,就也专门搞一个参数为(int, const T&)的构造函数就行。
在这里插入图片描述
测试一下:
在这里插入图片描述
完全ok。

然后看一下resize()这个函数。

resize

就考虑三种情况就行。

  1. n > capacity
    就是扩容加初始化

  2. size < n < capacity
    就是初始化

  3. n < size
    就是缩容

在这里插入图片描述

测试一下:

代码如下:

	vector<int> v1(8, 5);
	v1.resize(10);
	v1.resize(5);
	v1.resize(8);

上面的情况分别打印出来就是:
在这里插入图片描述
我们再看一下标准库中的和我们的一样不:
在这里插入图片描述
一样。

然后再看一下front和back。

front和back

很简单,返回值就是首尾元素。而且是引用。

front

在这里插入图片描述
测试一下:
在这里插入图片描述

back

在这里插入图片描述
测试一下:
在这里插入图片描述

然后再讲点关于深拷贝的问题。
不知道各位看我前一篇vector简介没,那篇最后我给了一道题,就是杨辉三角,这里需要用一下这个。

就是二维数组,用vector实现的二维数组。

借用一下那道题的代码:

class Solution {
public:
	vector<vector<int>> generate(int numRows) {
		vector<vector<int>> res;
		res.resize(numRows);
		for (int i = 0; i < res.size(); ++i)
		{
			res[i].resize(i + 1);
			res[i].front() = 1;
			res[i].back() = 1;
		}

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

		return res;
	}
};

在这里插入图片描述
这里运行起来的话,会直接崩掉。
在这里插入图片描述

调试起来发现是最后析构的时候错了。
在这里插入图片描述

看一下返回值:
在这里插入图片描述

如果我们把返回值改为void会怎么样:
在这里插入图片描述
运行起来了。

然后我们再来搞一个顺序表来接收一下最后的二维数组。
在这里插入图片描述

调试起来最终还是在析构处崩掉了。
在这里插入图片描述
不卖关子了,讲:

我们调试起来发现:
在这里插入图片描述
虽然外层的vector是深拷贝。
但是内层的那个vector是浅拷贝:
在这里插入图片描述
画出来图的话,就是这样:
在这里插入图片描述
蓝色区域的部分,在析构的时候会被释放两次。

此时就会导致程序崩溃。
怎么避免呢?
看一下我们的拷贝构造函数:

在这里插入图片描述

memcpy不能用,内部的成员会发生浅拷贝。因为memcpy是逐字节的拷贝。拷贝完后的数据是完全一样的。_start[i] = v._start[i]就不是了,当_start是vector的时候就会去先调用拷贝构造来对_start内部的成员赋值,然后再调用赋值重载,给_start整体赋值。

所以得要换成for循环来逐个赋值。
在这里插入图片描述
这样的话就能实现深拷贝。

也就是这样:
在这里插入图片描述

上面的拷贝构造用的是普通版本的拷贝构造。
我们的reserve也是有问题的。也要改成for循环的。
在这里插入图片描述
我们其他的写法就是用了reserve,如果没有改reserve的话就会崩掉。

再看一下二维的顺序表的图:
在这里插入图片描述

就讲到这吧,这个模拟实现讲的挺多的了。

到此结束。。。

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

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

相关文章

TI系列——CC2340B1 Uniflash烧录指南

一、物料准备 1.1 硬件准备 1、XDS110-PLUS2.0或者TI LaunchPad&#xff1a; 图 1 XDS110-PLUS2.0中包含&#xff1a; XDS110调试器、USB-A to Type-C-B电缆、具有2x5 2.54mm连接器的10引脚扁平电缆、具有2x5 1.27mm连接器的10引脚扁平电缆、具体请看说明指南文档。 图 2 上…

【MyBatis-Plus】DML编程控制 代码生成器(文末赠书)

1&#xff0c;DML编程控制 查询相关的操作我们已经介绍完了&#xff0c;紧接着我们需要对另外三个&#xff0c;增删改进行内容的讲解。挨个来说明下&#xff0c;首先是新增(insert)中的内容。 1. id生成策略控制 前面我们在新增的时候留了一个问题&#xff0c;就是新增成功后…

ArcGIS问题解决——CAD中的字体加载到ArcMap显示乱码

ArcGIS问题解决——CAD中的字体加载到ArcMap显示乱码 CAD中的字体加载到ArcMap显示乱码&#xff0c;是因为字符集的问题&#xff0c;解决办法即修改注册表中默认字符集为简体中文字符集即可。 ①winR,输入regedit&#xff0c;回车 ②找到计算机\HKEY_CURRENT_USER\SOFTWARE\…

网络变压器的工作原理

网络变压器又称“数据汞”&#xff0c;或网络绝缘变压器。在网络接口中起到两个主要作用&#xff1a; 一是通过将差模耦合和线圈耦合相结合的过滤器&#xff0c;增强PHY传输的差分信号的数据传输&#xff0c;并将电磁场转换为不同电平连接线的另一端&#xff1b; 二是隔离线连…

有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来​​

LEETCODE 1. 两数之和 题解地址 https://leetcode.cn/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-solution/ 有人相爱&#xff0c;有人夜里开车看海&#xff0c;有人leetcode第一题都做不出来。 题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0…

【运维】Linux的文件权限,文件所属组别,用户组访问文件的权限设置,将用户加入某个组,创建用户组

文章目录 文件权限添加一个组将用户加入组将文件设置为组访问更改所属用户查看Linux系统中用户所属的组 文件权限 添加一个组 添加一个组&#xff1a; 使用groupadd命令可以添加一个新的组。例如&#xff0c;要添加一个名为 “mygroup” 的组&#xff0c;可以运行以下命令&…

每日汇评:21日均线是黄金突破最大障碍,看涨形态逐步形成

1、美元在非农数据惨淡后持续上涨&#xff0c;金价维持上周涨幅&#xff1b; 2、美债收益率的反弹也抑制了金价的上涨空间&#xff1b; 3、金价在周五确认了一个看涨楔形&#xff0c;但重夺21日移动均线是关键&#xff1b; 黄金在本周初触及的1930美元关口下方徘徊&#xff0…

SciencePub学术 | 计算机决策类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 计算机决策类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 计算机决策类重点-01 【期刊简介】IF&#xff1a;4.0-4.5&#xff0c;JCR2区&#xff0c;中科院3区&#xff1b; 【出版社】世…

基于ChatGPT的企业微信机器人

1、openAI账号 登录OpenAI的账号后&#xff0c;再点击右上角的“Personal”图标&#xff0c;然后点击“view API keys”进入API页面。 点击“create new secret key”按钮。 生成秘钥之后&#xff0c;把秘钥复制下来。 2、拉取项目代码 git clone https://github.com/zhay…

安全与便利并行:智慧电梯的魅力与优势

数字孪生技术正在逐渐改变着我们的生活方式&#xff0c;其中之一就是在智慧电梯领域的应用。那么&#xff0c;数字孪生技术是怎样运用到电梯上的呢&#xff1f;这又会对我们的生活造成怎样的改变&#xff1f; 在智慧电梯中&#xff0c;数字孪生技术将电梯的物理实体与虚拟模型…

3. Springboot快速回顾(拦截器的使用)

本文将提供一个案例&#xff0c;回顾如何在springboot中使用拦截器。 首先明白为何使用拦截器&#xff0c;拦截的是什么&#xff1f; 比如你设计了一个网站&#xff0c;为这个网站设计了一个登陆界面后&#xff0c;希望在登录界面进行身份验证&#xff0c;进入系统。但我可以直…

flutter开发实战-多语言flutter intl

flutter开发实战-多语言flutter intl 之前做的应用中有用到多语言&#xff0c;一直没有整理&#xff0c;这里整理一下多语言设置流程。 使用的是Android studio 一、flutter_intl 插件 使用Android studio安装flutter_intl 插件&#xff0c;更新或者安装flutter_intl 插件后…

国产4 通道模拟复合视频解码芯片MIPI CSI 接口,XS9922B

XS9922B 是一款 4 通道模拟复合视频解码芯片&#xff0c;支持 HDCCTV 高清协议和 CVBS 标 清协议&#xff0c;视频制式支持 720P/1080P 高清制式和 960H/D1 标清制式。芯片将接收到的高清 模拟复合视频信号经过模数转化&#xff0c;视频解码以及 2D 图像处理之后…

Django_模板标签语法

目录 引用变量 for循环标签 if条件标签 with标签 注释 extends和block标签 csrf_token标签 load static标签 源码等资料获取方法 引用变量 可以使用{{}}引用视图函数响应的变量和模板中的变量。 比如有如下视图函数 在模板中引用变量方式如下 界面展示如下 for循环标…

微信小程序——真机调试步骤

工具/原料 手机 微信开发者工具 以下是微信开发者工具注册和安装教程&#xff1a; 微信开发者工具_小彭不会秃头的博客-CSDN博客 开始真机调试 前提&#xff1a;手机和电脑连同一个网络&#xff0c;电脑连手机热点没用 把网络ip添加到请求路径中&#xff08;HBuilder X软件 小…

docker部署rabbitmq 后访问管理首页常见问题

1.项目启动后 管理首页无法访问 1&#xff09;检查15672端口是否可以访问 2&#xff09;docker exec -it your_container_name /bin/bash 进入docker容器执行如下命令&#xff1a; 3) rabbitmq-plugins enable rabbitmq_management 2.访问首页时提示不是私密连接&#xff1a;…

单细胞生物实验教学三维vr仿真模拟实操平台增强学生的从业自信心

VR元宇宙近几年在各个领域得到了广泛的应用&#xff0c;特别是教育领域&#xff0c;干细胞是具有自我更新能力和分化为多种细胞类型的能力的一类细胞&#xff0c;具有极高的医学研究价值和应用前景。干细胞冻存技术是一种保护干细胞的重要方法&#xff0c;也是干细胞应用的前提…

微信小程序做登录密码显示隐藏效果 并解决安卓手机端隐藏密码时小黑点显示过大问题

在编辑器和苹果手机上面显示就是正常的大小&#xff0c;在安卓手机上面黑点就非常大&#xff0c;需要单独调 安卓手机显示比较大 wxml 注意&#xff1a;在html中的input是通过切换type的属性值来实现隐藏显示的 在微信小程序的input里面type没有password属性 是通过password属…

消除企业信息孤岛的低代码开发平台

企业数字化转型上&#xff0c;信息孤岛是企业痛点之一。所谓的信息孤岛&#xff0c;指的是企业内部使用着多套应用软件&#xff0c;多年后企业员工会在多套系统中积累大量的企业各类数据资产&#xff0c;由于各系统数据不能互通&#xff0c;随即形成一座座数据孤岛&#xff0c;…

pytorch学习第一篇:conda配置jupyter notebooks pytorch

安装jupyter notebooks 创建一个pytorch的环境 conda create -n pytorch python3.10 conda activate pytorch安装jupyter notebook&#xff0c;运行命令 conda install jupyter notebook启动jupyter 运行命令 jupyter notebook或者 notebook查看pyhton版本 import sys p…