【C++学习】vector的使用及模拟实现

news2024/9/29 5:31:01

🐱作者:一只大喵咪1201
🐱专栏:《C++学习》
🔥格言:你只管努力,剩下的交给时间!
tu

vector的使用及模拟实现

  • 🎇构造函数
    • 🧨模拟实现
    • 🧨vector的扩容机制
    • 🧨模板参数推演
  • 🎇vector与容量有关的接口
  • 🎇vector的常用接口
    • 🧨查
      • find
    • 🧨增
    • 🧨删
    • 🧨改
  • 🎇迭代器失效问题
  • 🎇更深层次的深拷贝
  • 🎇总结

在学习了string以后,我们对模板有了一定的了解,下面本喵来给大家介绍一下STL模板中的vector。

vector其实就是顺序表,它是在管理数组,并且它是一个类模板,可以实例化为不同类型的类,来供我们使用。STL标志模板库给我们提供了很多的成语函数接口来供我们使用,使我们编程的效率大大提高。

本喵在介绍它使用的同时,也会讲解它的底层原理,来模拟实现它,好让我们对vector有一个更深的了解。

🎇构造函数

图
官方库中提供上图所示的几种重载的构造函数,根据它们的函数声明就可以知道它们是如何使用的。

图
同样地,还提供了push_back函数来向vector中插入数据。
图

其中,形成类型const value_type&就是T&,T是模板参数,在实例化的时候可以是内置类型,也可以是自定义类型。

图
上图代码,向vector中插入数字,并且通过范围for打印出来。

  • vector后的<>中的内容就是模板参数,可以是内置类型,如int,char,float等等
  • 也可以是自定义类型,比如Date,甚至是vector等自定义类型。

🧨模拟实现

vector的成员变量不和string的一样,并不是_size,_capacity_等,而是三个指针。

本喵来给大家看一下SJI版本的STL源码:

图
首先可以看到,vector是一个模板类,typedef的一些类型也是我们后面经常会用到的。

图
可以看到,它的成员变量只有三个,而是三个迭代器。

图
在模拟实现的时,将我们自己模拟的vector放在自己的命名空间wxf中,图中红色框中的内容是没有参数的默认构造函数。

图
为了向模拟实现的vector中插入数据,需要我们自己实现push_back()函数,如上图中所示。

  • 红色框中是为了扩容而进行的三目运算。
  • 当vector刚刚创建时,它里面是没有任何内容的,此时它的容量是0,此时需要给一个初始容量,这里本喵将其设置为4.
  • 当vector中的容量不够,并且不是0时,进行二倍扩容,将容量变成原来的二倍。

图
还需要实现上图所示的俩个函数来辅助。

图
为了能够使用reserve函数来扩容,需要我们自己来模拟实现reserve函数,如上图所示。

  • 红色框中,需要提前记录一下当前vector的size。
  • 否则扩容后的_finish就会是0,因为temp加的值会是size(),而这个size是此时求出来使用的,由于_start已经发生了变化,所以求出来的值是一个和_start大小相等,符合相反的数。

可以自己去尝试一下,看看发生什么样的错误。

图
此时就初步实现了vector和push_back。

🧨vector的扩容机制

vector个string一样,也是动态变化的数据结构,所以就会存在扩容,下面本喵来给大家看看vector在不同平台下的扩容机制。

vs2019平台:

图
向vector中插入100个数据,当size和capacity相同的时候打印当前容量,因为此时会发生扩容,可以看到,每次扩大到之前容量的1.5倍左右。

图
同样的代码,在g++编译器下就是按照2倍来扩容的。

本喵这里的扩容采用的是g++的机制,也就是严格按照之前容量的2倍来进行扩容。

图
除了没有参数的默认构造函数,还有使用迭代器区间来初始化类对象的构造函数。如上图所示,v2成功的用迭代器区间进行构造。

下面本喵来模拟实现一下它:

图
在模拟之前,需要实现上图中的俩个接口。

图
在类模板中,只要有需要是可以继续套模板的,如上图中的红色框中内容。

  • 这里使用模板的原因是,为了通过迭代器区间来实现,至于迭代器的类型并没有固定为指针。
  • 在vector和string中,迭代器的本质就是指针,但是在列表等其他数据结构中,迭代器的本质就不是指针了,所以这里使用的是泛型编程。

🧨模板参数推演

图
上图模拟实现的是使用n个T来构造类对象的构造函数。

  • 红色框中给是一个缺省值,该缺省值是T的匿名对象
  • 如果是内置类型,比如int,它同样有默认构造函数,初始化后该int类型变量的值为0.
  • 如果是自定义类型,在创建匿名对象的时候会调用它的默认构造函数。

图
执行上图中的代码:

图
在编译的时候,报了一个非法间接寻址的编译错误。

图
但是使用char来实例化vector,并且使用该构造方式创建对象的时候,就不再报错,而且创建成功了,这是什么原因呢?

图

  • 当使用int将vector实例化以后,构造函数的模板产生T就成了int类型。
  • 在外部将int类型的5传给构造函数以后,由于第一个形参是size_t类型的,所以需要发生整型提升。
  • 但是编译器此时认为,传给构造函数的俩个参数都是int类型,而此时构造函数的俩个形参一个是size_t,一个是int类型,要想匹配还需要整型提升第一个参数,比较麻烦。
  • 编译器是比较懒的,不想多干活,所以它发现,下面的使用迭代器区间的构造函数,可以将模板参数推演为int类型,以此来供传过来的俩个int类型使用。
  • 所以在函数内,对int类型解引用就发生了错误的间接寻址错误。

那么为什么,使用char类型来实例化vector就不会发生这个错误呢?

  • 因为此时构造函数中的T被指定成了char类型,而另一个传过来的实参是int类型。
  • 一个int类型,一个char类型,编译器为了少干活,就没有使用迭代器区间的构造函数,也就是没有推演模板参数,而是采用了将第一个参数整型提升为size_t的构造函数。

编译器也是懒狗,它会寻找工作量最少的方式来实现用户的要求,也就是会根据数据类型自行决定是推演模板参数类型,还是使用已有的函数。

图
解决这个问题的办法也是很简单,只需要重载一个int类型的构造函数即可,如上图红色框所示,此时模板参数实例化为int类型也不再报错。

拷贝构造函数:

构造函数学习了以后,按照成员函数类型,还需要有拷贝构造函数。

图
STL库中的拷贝构造函数声明如上,它的形参是一个vector的引用。

图
如上图所示,使用v1来构造v2,可以看到v2中的内容和v1一模一样。

模拟实现拷贝构造函数:

图
上图所示的是拷贝构造函数的现代写法,也就是抓壮丁,在string的模拟实现时,本喵详细讲解过。

这里使用的swap函数不是标准库中的,所以需要我们自己实现:

图

  • 拷贝构造函数中的交换函数,之所以不使用标准库中的sawp,是为了减少系统的开销。
  • 如果模板参数T是一个自定义类型的时候,使用库中的swap代价就会非常大,因为自定义类型在这个过程中会发生拷贝。
  • 在模拟实现的swap中再使用库中的swap时,仅仅是指针变量直接的交换,发生拷贝也就4个字节大小,代价并不大。

图
必须先实现俩个const迭代器的成语函数,如上图所示,因为拷贝构造的形参x是const类型的vector,此时它的this指针是被const修饰的,它的成语函数begin和end得到的迭代器也必须是被const修饰的,否则就会发生权限的放大,是不被允许的。

图
顺带着再实现一下析构函数,非常简单,本喵就不作讲解了。

赋值运算符重载函数:

图
赋值运算符重载函数只有一个,并没有多个重载类型。

图
如上图所示,成功的将v1赋值给了v2,其实这样看来,赋值运算也是属于构造的一种。

赋值运算符重载的模拟实现:

图
这里的形参不能使用引用,否则会将赋值的对象改变。这里会发生拷贝构造,创建出一个新的对象v,但是这个v不在栈区上,而是在堆区上,将this指针指向的内容和v进行交换。

🎇vector与容量有关的接口

图
上图中的接口全部都是和容量有关的,如size,capacity,reserve接口在前面介绍构造函数的时候已经介绍了,下面本喵来介绍一下其他没有介绍的。

resize:

图

vector的resize和string的resize是一样的。

  • size < n < capacity:仅调整size,也就是只改变_finish的值。
  • n > capacity:size和capacity都发生了改变,也就是发生了扩容
  • n < size:仅调整size,只改变_finish。

原则: 缩容的时候值改变size,不改变capacity,也就只调整_finish,不动_endofstorage。这是一种以空间换时间的思想。

resize的模拟实现:

图

在将三种情况实现出来后,resize便实现了,如上图所示。

empty:
图
如上图,当vector是空的时候,接口empty()的返回值是真,反之为假。

empty的模拟实现:

图
只要_finish和_start是相同的,就说明此时的vector是空的。

shrink_to_fit:

图
原本v1的size是1,capacity是10,在使用了shrink_to_fit以后,size和capacity相等了。

shrink_to_fit的作用就是将capacity变的和size一样的,也就是进行缩容,是一种以时间换空间的做法。

该接口是C++11才有的接口,本喵暂时就不进行模拟实现了。

🎇vector的常用接口

图
STL中的vector除了上面提到的一些属性类的接口外,还有一些操作类的接口,也就是我们常说的增删查改,这也是一个数据结构中最核心的接口。

🧨查

访问也是查的一种形式,而且访问是非常重要的,本喵来先给大家介绍一下vector的访问接口。

[]运算符重载:
[]在string详细讲解过,这里不多啰嗦,直接看演示:

图
可以看到,可以像访问数组一样去访问vector。

[]的模拟实现:

图
要严格检测是否发生越界。

图
可以看到,无论是写还是读都可以实现。

at:

图

可以看到,at的作用其实是和[]一样的,那么为什么又要有[]存在呢?

  1. []的可读性比at高

这一点毋庸置疑,我们肯定是喜欢阅读带有[]的代码,因为这样可以像访问数组一样来访问vector,而不是像at一样是函数调用,虽然本质上是一样的。

  1. 发生越界行为时,[]发生的断言错误,at是抛异常

[]是使用assert来防止越界的,而at在发生越界时会抛异常,并不会强制性的让程序停止。

头部和尾部数据的访问:

图
图
虽然使用[]也可以实现头部和尾部数据的访问,但是使用front和back的时候是不用知道尾部和头部下标的。

front和back的模拟实现:
图
front和back都是有重载函数的,一个是可读可写的,另一个是只读的,此时用const修饰了该接口函数,包括返回的也是被const修饰的引用类型,所以是不可以修改的。

find

在vector的STL中是没有提供find函数的,因为除string以外,其他容器查找的都是数据结构中的一个成员,并不是字符串之类的,所以这些数据结构共用一个find接口就可以,这个接口在官方提供的算法库中。

图

可以看到,在官方的文档中,连源码都给我们了,它就是通过迭代器来查找的指定元素的。

  • 查找到指定元素后,返回该元素的迭代器。
  • 没有找到指定元素时,返回该容器最后一个元素的下一个位置的迭代器,也就是end()。

图
使用find的查找情况如上图所示。

🧨增

push_back就是一个非常典型的增,也是我们使用最多的,在前面本喵已经给大家详细介绍过了,并且也模拟实现了。除了这个以外,还有能够在任意位置插入的insert。

insert:
图
在官方提供的STL库中,insert有三个重载函数。

图
上图中,演示了使用insert在指定位置插入一个元素,插入多个元素,插入一段迭代器区间。

insert模拟实现:

虽然有多个接口,但是本喵只模拟实现一个:

图
可以看到,任意位置插入的接口中,存在着数据的挪动,如果头插一个元素的时候,需要将所有元素向后移动一个位置,代价是很大的。所以vector不建议进行频繁的头插,头插的时间复杂度是O(N2)。

图
可以看到,我们模拟实现的insert成功的实现了插入。

🧨删

erase:

图
erase函数有俩个重载函数。

图

  • 使用算法库中的find找到3所在位置的迭代器,使用erase将该位置元素删除。
  • 将v2中除第一个和最后一个元素外都删除。

erase模拟实现:

这里仅实现一个删除指定位置的erase。

图
在删除以后,同样会发生数据的挪动。

图
成功删除了指定位置的内容。

clear:

图
clear的作用也是删除,但是它是将vector中的所有内容都删除,并且保留vector。

图
使用clear清空vector以后,size为0,但是capacity仍然保持不变。

clear的模拟实现:

图
实现起来非常简单。

pop_back:

图
仅有一个pop_back元素,没有重载函数。

图
该函数的作用就是将最后一个元素删除。

pop_back的模拟实现:

图
同样实现起来非常简单。

🧨改

改就是从vector中找到某个元素,然后将其进行替换,一般都是先使用find找到某个元素,然后再使用[]或者at接口进行访问并修改。这些接口本喵在前面都讲解过,这里介绍一下没有讲解过的。

assign:

图
图
assign的作用就是将vector中原本的内容全部用新内容替换掉。该接口的使用频率并不高,本喵这里就不进行模拟实现了。

🎇迭代器失效问题

  • insert内部由于扩容引起的迭代器失效(野指针):

tu
使用我们模拟实现的insert在2的前面插入了一个100,此时是没有任何问题的,而且也是成功插入了。

图
此时重复上面的操作就崩溃了,这是什么原因呢?

首先我们来看俩次操作的不同之处:

  • 第一次插入数据时,vector中原本有3个数字,插入100后成了4个数字。
  • 第二次插入数据时,vector中原本有4个数字,插入100后成了5个数字。

俩次插入时,由于vector中原本的数据个数不同,所以发生扩容的情况也不同。第一次插入是不用扩容的,第二次插入需要扩容后才能插入。

图
第一次插入如上图所示,只需要将2和3向后移动一个位置,然后在空出来的pos处插入100即可。

图
第二次插入时,此时vector的容量已经满了,所以需要扩容后再插入数据。扩容时,开辟了一块新的空间,并且将原本空间释放了,但是pos指向的位置仍然是原本的位置。

  • 此时pos就成了野指针,也就是这里所讲的迭代器失效了。

为了避免这个问题,在扩容后需要更新一下pos的内容。

图
只需要让pos指向新位置的2即可,如上图所示。

图

在代码中进行如上图所示的操作即可。其中还增加了返回值,返回的就是pos迭代器,因为发生扩容以后pos的位置是会改变的,否则就找不到新的pos位置了。

  • erase之后引起的迭代器失效:

图
在VS2019上,当it迭代器处的位置被删除以后,it迭代器就不能再使用了,无论是读还是写,如上图所示,虽然没有直接报错,但是返回代码如红色框所示,说明它还是出错了。

图
同样的代码,在g++编译器下就不会报任何错误。这是好事还是坏事呢?

图
vector中的内容是1,2,3,4,删除其中的偶数。

图

成功删除了其中的偶数,但是这种操作在VS2019中是不被允许的。

  • 为了在任何平台下我们对代码都能够跑过去,在这里我们认为,使用erase删除指定位置后的迭代器是失效的,是不能再使用的。

那如果我们就要访问删除后迭代器的位置呢?

图

和insert一样,erase返回pos迭代器,此时返回的迭代器是不失效的,可以使用的。

图
如上图所示,erase返回的迭代器是可以使用的。

🎇更深层次的深拷贝

图

  • vector的vector相当于是一个二维数组。

此时运行结果是正常的。

图
当插入第五个的时候,发生了错误,可以看到,打印出来的结果是乱的。

原因分析:

图

原本v2的结构如上图所示,v2中的四个元素分别指向一个vector。

图
在v2中插入第五个vector的时候发生了扩容。

图
而我们模拟的扩容是通过memcpy来复制原本vector中的内容,memcpy的机制是按照字节一个一个的复制。

  • v2在扩容的时候,创建了新的空间,是原来的2倍。
  • 将原本v2空间中的内容按字节复制到了新的v2空间。
  • v2中存放的内容本质是都是指针,所以按照字节复制以后,指针的内容并不会发生改变。
  • 此时v2的新空间中的指针和就空间中的指针指向的是相同的vector。
  • 并且memcpy以后会释放原本v2中指针指向的这些vector。此时v2新的空间中的指针指向的vector也就成了被释放的空间了。

这里在扩容的时候虽然发生了一次深拷贝,但是不够,需要进行更深一层的拷贝。

图
也就是让v2新空间中的指针指向新的vector,如上图中蓝色线所指。此时原来旧的vector便可以被释放了。

使用memcpy进行拷贝是行不通的。

图
如上图中红色框所示,这里使用重载的赋值运算符来复制原本v2中的内容。因为它会在赋值的过程中,给v2中的指针指向的vector开辟新的空间,就不怕旧的vector被释放了。

图
此时即使插入第五个vector也不会出错了。

🎇总结

在有了string的基础以后,vector的使用还是非常容易的,所以在这篇文章中本喵采用了vector的使用和模拟并行的方式。对每一种数据结构的模拟实现,并不是为了造一个更好的轮子,而是为了能够对底层有更深的了解,从而能够更好的使用官方提供的这些数据结构的模板库。

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

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

相关文章

力扣(LeetCode)363. 矩形区域不超过 K 的最大数值和(2022.12.30)

给你一个 m x n 的矩阵 matrix 和一个整数 k &#xff0c;找出并返回矩阵内部矩形区域的不超过 k 的最大数值和。 题目数据保证总会存在一个数值和不超过 k 的矩形区域。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,0,1],[0,-2,3]], k 2 输出&#xff1a;2 解释&…

2022年-年度总结报告

目录1.攻克的技术难题问题1&#xff1a;2.学习的新技术1.system系统的学习2.网络3.游戏22年总结23年的计划1.先给自己画个大饼2.计划内的小饼1.攻克的技术难题 问题1&#xff1a; 跑VTS测试的时候&#xff0c;mkfs.exfat挂测失败&#xff0c;VTS刷最新的谷歌gsi没有过&#x…

大文件传输如何帮助媒体行业

过去几年&#xff0c;随着分辨率从4k到6k再到8k的升级&#xff0c;观众已经适应了高分辨率的时代。然而&#xff0c;许多媒体工作室的工作流程还停留在过去。 TB甚至PB大小的材料的传输让从业者无所适从。这就是高速文件传输对媒体行业有很大帮助的原因。 什么是大文件传输&am…

81.【SpringMVC】

SpringMVC(一)、认识MVC三层架构1.回顾MVC(1).什么是MVC三层框架(2).MVC要做那些事情?(3).常见的MVC三层框架结构(4).知识拓展2.代码回顾3.什么是SpringMVC(二)、第一个SpringMVC0.前提1.搭建环境2.配置WEB-INF的XML配置文件3.在资源Resource的包下设置springmvc-servlet.xml4…

30-深入Hotspot源码与Linux内核理解NIO/BIO/AIO

IO模型 IO模型就是说用什么样的通道进行数据的发送和接收&#xff0c;Java共支持3种网络编程IO模式&#xff1a;BIO&#xff0c;NIO&#xff0c;AIO BIO(Blocking IO) 同步阻塞模型&#xff0c;一个客户端连接对应一个处理线程 缺点&#xff1a; 1、IO代码里read操作是阻塞操…

Spreadsheet与FineReport数据集对比

什么是数据集&#xff1f;在BI工具中指的是在报表开发前的取数过程&#xff0c;把需要的数据整合成一个数据集合&#xff0c;以便于在报表开发中使用。可以把它理解为我们基于数据库获取我们需要的数据。而数据库获取数据是有多种方式的&#xff0c;比如可以通过直接写SQL语句、…

基于verilog实现序列相关检测

题目来源牛客网&#xff0c;完整工程源码&#xff1a;https://github.com/ningbo99128/verilog 目录 1、VL25 输入序列连续的序列检测 题目介绍 思路分析 代码实现 仿真文件 2、VL26 含有无关项的序列检测 题目介绍 思路分析 代码实现 仿真文件 3、VL27 不重叠序列检…

Sleuth+Zipkin架构

为什么要链路追踪 小结&#xff1a; nacos 【name server】&#xff1a;注册中心&#xff0c;解决服务的注册与发现 nacos【config】&#xff1a;配置中心&#xff0c;微服务配置文件的中心化管理&#xff0c;同时配置信息的动态刷新 Ribbon&#xff1a;客户端负载均衡器&#…

《设计模式》享元模式

《设计模式》享元模式《设计模式》设计模式的基本原则 《设计模式》单例模式 《设计模式》工厂模式 《设计模式》原型模式 《设计模式》建造者模式 《设计模式》适配器模式 《设计模式》桥接模式 《设计模式》装饰者模式 《设计模式》组合模式 《设计模式》外观模式 《设计模式…

数据结构课设:迷宫问题

文章目录前言一、概要设计1、基本信息2、功能模块图3、功能描述4、调用关系图5、结果演示① 创建迷宫② 求解③ 清除多余路径二、完整代码前言 最近刚好在写自己的课设&#xff0c;匆匆忙忙写出来的课设系统&#xff0c;仍有不足&#xff0c;拿出来和大家分享一下&#xff0c;…

C. p-binary(二进制位)

Problem - 1225C - Codeforces Vasya会看中任何数字&#xff0c;只要它是2的整数次方。另一方面&#xff0c;Petya非常保守&#xff0c;只喜欢单一的整数p&#xff08;可以是正数、负数或零&#xff09;。为了结合他们的口味&#xff0c;他们发明了2xp形式的p-二进制数&#xf…

jmeter接口测试之导入测试用例/get请求中Url存在参数(工作日记2)

导入接口用例进行接口测试 以运营中心测试计划中的企业菜单管理为例 【前提条件】 1、有接口数据 2、有接口用例 我们需要把接口测试用例转换为CSV格式步骤如下&#xff1a; 右键选择打开方式为Notepad 需要将文件设置一下编码 文件留着备用 【步骤】 1、新建一个企业…

前端基础之PS和相关基础知识总结

PS安装&#xff1a; 淘宝 PS简介 面板简介&#xff08;菜单栏、工具栏、辅助面板&#xff09; 图片格式&#xff1a; jpgjpeg&#xff08;色彩丰富的图片&#xff09;png&#xff08;半透明图片&#xff09;gif&#xff08;网页动态图&#xff09;psd&#xff08;ps设计源文…

android java udp广播 用于局域网搜索扫描设备。

DatagramSocket socket new DatagramSocket();//设置接收等待时长socket.setSoTimeout(LanCommConfig.RECEIVE_TIME_OUT);byte[] sendData new byte[1024];//使用广播形式&#xff08;目标地址设为255.255.255.255&#xff09;的udp数据包DatagramPacket sendPacket new Dat…

STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示

STC15单片机+DS18B20+LCD1602+PCF8574转IIC接口温度显示 📺显示效果 LCD1602 IIC接口 ✨本示例基于自制的STC开发板,主控采用的:IAP15W4K61S4相关篇《【PCB开源分享】STC/IAP15W4K61S4开发板》《【开源分享】自制STC15W408AS开发板》🌼接线说明 🌿CD1602+PCF8574转IIC接…

stlink下载调试器使用说明(STM32采用stlink下载程序)

stlink能干什么&#xff1f; 最基本的功能&#xff1a;下载程序。 一般STM32支持ISP串口下载&#xff0c;也支持stlink、jlink等下载器下载 。 使用stlink、jlink下载要比串口方便很多&#xff0c;在keil里直接点击下载就行了&#xff0c;不需要去选择hex文件&#xff0c;速度…

你可能还不知道 golang 的高效编码细节

xdm&#xff0c;我们都知道 golang 是天生的高并发&#xff0c;高效的编译型语言 可我们也都可知道&#xff0c;工具再好&#xff0c;用法不对&#xff0c;全都白费&#xff0c;我们来举 2 个常用路径来感受一下 struct 和 map 用谁呢&#xff1f; 计算量很小的时候&#xf…

特斯拉第10000桩落成,百度地图送10000名充电免单!

「导语」 为庆祝特斯拉在中国大陆落成第10000个超级充电桩&#xff0c;百度地图推出特斯拉车主专属权益。车主们通过百度地图APP&#xff0c;即可实时获取超级充电桩空闲信息、一键规划专属充电路线。12.28-1.2期间&#xff0c;更有10000份充电免单福利限时放送。 在汽车电动…

Linux环境开机自启java应用

Linux环境设置开机自启java应用&#xff08;Linux定时任务&#xff09; crontab是Unix和Linux用于设置需要周期性被执行的指令&#xff0c;是Linux服务器很常用的技术&#xff0c;很多任务都会设置在crontab循环执行。 crontab &#xff1a; 提供给用户控制任务计划的命令&…

WebDAV之葫芦儿·派盘+File Manager

File Manager 支持WebDAV方式连接葫芦儿派盘。 手机文件太多,空间不足、隐藏文件多、文件清理不干净?推荐您一个功能强大的文件管理器,可以让你对手机中的各类文件进行管理,支持快速移动、复制粘贴、压缩解压等等。同时还能对已经安装的程序进行卸载,自动识别手机中的AP…