Written on 2023-01-16
个人学习智能指针记录合集:
std::shared_ptr 共享智能指针,也被称为计数智能指针。
共享智能指针会记录有多少个共享智能指针指向同一个对象,当这个数为 0 的时候,程序自动的默认释放(析构)这个对象,记录有多少个的这个方法叫做引用计数。
共享智能指针可以有多个共享智能指针同时管理同一个对象。
#include <iostream>
#include <memory>
using namespace std;
class Person{
public:
Person(){ cout << "Constructor: person's age = " << m_age << endl; }
Person(int age) : m_age(age){ cout << "Constructor: person's age = " << m_age << endl; }
~Person(){ cout << "Destructor: person's age = " << m_age << endl; }
void getAge(){ cout << "Person's age = " << m_age << endl; }
private:
int m_age = 0;
};
int main()
{
{
Person *p = new Person(18);
}
cout << endl << "Before return" << endl;
return 0;
}
/** 输出:
Constructor: person's age = 18
Before return
**/
依输出可见,p指向的对象并没有被析构,因为并没有析构函数中的打印,这造成了内存泄漏。
shared_ptr智能指针管理// ...
int main()
{
{
shared_ptr<Person> sPtr1 {new Person(18)};
}
cout << endl << "Before return" << endl;
return 0;
}
/** 输出:
Constructor: person's age = 18
Destructor: person's age = 18
Before return
**/
依输出可见,离开了程序块的作用域后,析构函数中的打印体现出来了,sPtr1管理的对象自动的被析构了。
long use_count() const noexcept;
当 shared_ptr 为 nullptr 时,返回为 0。
shared_ptrconstexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
std::nullptr_t:空指针nullptrshared_ptrtemplate< class Y >
explicit shared_ptr( Y* ptr );
Y:动态分配的对象的类型// ...
int main()
{
shared_ptr<int> sPtr1; // 创建空管理的shared_ptr
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
shared_ptr<int> sPtr2(nullptr); // 创建空管理的shared_ptr
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
shared_ptr<int> sPtr3(new int(100)); // 创建对非数组对象管理的shared_ptr
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
shared_ptr<int> sPtr4(new int[10]); // 创建对数组对象管理的shared_ptr
cout << "sPtr4 use count = " << sPtr4.use_count();
return 0;
}
/** 输出:
sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 1
sPtr4 use count = 1
**/
解释:
sPtr1和sPtr2都为nullptr,故引用计数都为 0;
sPtr3和sPtr4所管理的对象,都只有一个 shared_ptr 管理,故引用计数都为 1。
shared_ptrtemplate< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );
template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );
template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc );
template< class Deleter, class Alloc >
shared_ptr( std::nullptr_t ptr, Deleter d, Alloc alloc );
Deleter:删除器,在引用计数为 0 时,shared_ptr 会自动的执行删除器Alloc:分配器(Allocator)delete ptr 为默认删除器delete[] ptr 为默认删除器void deletePtr(T* p){ ... }
例,对于文件的使用场景,不是直接delete文件,而是关闭文件:
// ...
void closeFile(FILE* fp)
{
if (fp == nullptr) return;
fclose(fp);
cout << "File closed" << endl;
}
int main()
{
FILE* fp = fopen("data.txt" ,"w");
shared_ptr<FILE> sfp{fp,closeFile};
if (sfp == nullptr)
cout << "Failed opened" << endl;
else
cout << "File opened" << endl;
return 0;
}
shared_ptr主要是通过复制构造函数和移动构造函数std::move()。
shared_ptr( const shared_ptr& r ) noexcept;
template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;
shared_ptr( shared_ptr&& r ) noexcept;
template< class Y >
shared_ptr( shared_ptr<Y>&& r ) noexcept;
例,
// ...
int main()
{
shared_ptr<int> sPtr1(new int(100));
cout << "sPtr1 use count = " << sPtr1.use_count() << endl << endl;
// 创建通过现有现有共享智能指针的 shared_ptr
// 通过拷贝构造函数创建
shared_ptr<int> sPtr2(sPtr1);
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl << endl;
// 通过赋值运算符创建
shared_ptr<int> sPtr3 = sPtr1;
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl << endl;
// 通过移动构造函数创建
shared_ptr<int> sPtr4(move(sPtr1));
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
std::shared_ptr<int> sPtr5 = move(sPtr2);
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
cout << "sPtr4 use count = " << sPtr4.use_count() << endl;
cout << "sPtr5 use count = " << sPtr5.use_count();
return 0;
}
/** 输出:
sPtr1 use count = 1
sPtr1 use count = 2
sPtr2 use count = 2
sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3
sPtr1 use count = 0
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 3
sPtr1 use count = 0
sPtr2 use count = 0
sPtr3 use count = 3
sPtr4 use count = 3
sPtr5 use count = 3
**/
解释:
sPtr2、sPtr3都是通过对sPtr1执行了复制构造函数,因此sPtr1的引用计数一次增加 1;sPtr2、sPtr3同理。
通过move(sPtr1),使得sPtr1释放了被管理对象的所有权,此时sPtr1被设置为nullptr,因此sPtr1引用计数为 0;sPtr2同理。
std::make_sharedtemplate< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
T:非数组,指针指向的数据类型Args&&... args:T 的构造函数参数列表这种初始化效率更高,在 C++17 之前的编译器更安全
// ...
int main()
{
shared_ptr<int> sPtr1 = make_shared<int>(100);
shared_ptr<int> sPtr2(make_shared<int>(200));
shared_ptr<int> sPtr3{make_shared<int>(300)};
return 0;
}
.reset( ptr )使用.reset( ptr ),使得释放对shared_ptr原管理对象的所有权,转为对新对象管理的所有权。
void reset() noexcept;
template< class Y >
void reset( Y* ptr );
template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );
template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );
注意,.reset( ptr )若传入的所指向的对象已被占有,程序会异常。
// ...
int main()
{
shared_ptr<int> sPtr1(new int(100));
shared_ptr<int> sPtr2 = sPtr1;
shared_ptr<int> sPtr3 = sPtr2;
shared_ptr<int> sPtr4 = sPtr1;
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
sPtr4.reset();
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
cout << "sPtr4 use count = " << sPtr4.use_count() << endl << endl;
sPtr3.reset(new int(100));
cout << "sPtr1 use count = " << sPtr1.use_count() << endl;
cout << "sPtr2 use count = " << sPtr2.use_count() << endl;
cout << "sPtr3 use count = " << sPtr3.use_count() << endl;
cout << "sPtr4 use count = " << sPtr4.use_count();
std::shared_ptr<int> sPtr5;
sPtr5.reset(sPtr1.get()); // 异常
return 0;
}
/** 输出:
sPtr1 use count = 4
sPtr2 use count = 4
sPtr3 use count = 4
sPtr4 use count = 4
sPtr1 use count = 3
sPtr2 use count = 3
sPtr3 use count = 3
sPtr4 use count = 0
sPtr1 use count = 2
sPtr2 use count = 2
sPtr3 use count = 1
sPtr4 use count = 0
**/
解释:
sPtr1-4的引用计数均为 4。sPtr4使用了reset()后,释放被管理对象的所有权,被管理对象的shared_ptr个数减 1,sPtr4被设置为nullptr,同时,sPtr1-3的引用计数均变为 3。sPtr3使用了reset(new int(100))后,释放原被管理对象的所有权,被管理对象的shared_ptr个数减 1,sPtr4被设置为新的管理对象int(100),同时,sPtr1-3的引用计数均变为 2。sPtr5的reset传入的所指向的对象已被占有,程序异常,没有正常结束。.reset() 释放被管理对象的所有权用于取消shared_ptr对管理对象的所有权;
当这个对象被shared_ptr管理的数量为 0,会执行删除器。
使用.reset(),会使得shared_ptr设置为nullptr。
T* get() const noexcept;
operator* and operator->可以像普通指针一样,使用shared_ptr对所管理的对象进行访问。
// ...
int main()
{
shared_ptr<int> sPtr1 {new int(100)};
cout << *sPtr1 << endl << endl;
int *i = sPtr1.get();
cout << *i << endl;
cout << *sPtr1 << endl << endl;
*i = 200;
cout << *i << endl;
cout << *sPtr1 << endl << endl;
*sPtr1 = 300;
cout << *i << endl;
cout << *sPtr1;
return 0;
}
/** 输出:
100
100
100
200
200
300
300
**/
// ...
int main()
{
shared_ptr<Person> sPtr1 {make_shared<Person>()};
shared_ptr<Person> sPtr2 {make_shared<Person>(18)};
shared_ptr<Person> sPtr3 {make_shared<Person>(22)};
shared_ptr<Person> sPtr4 = sPtr1;
sPtr1.reset();
sPtr2.reset();
sPtr3.reset(new Person(19));
cout << endl << "Before return" << endl;
return 0;
}
/** 输出:
Constructor: person's age = 0
Constructor: person's age = 18
Constructor: person's age = 22
Destructor: person's age = 18
Constructor: person's age = 19
Destructor: person's age = 22
Before return
Destructor: person's age = 0
Destructor: person's age = 19
**/
解释:
shared_ptr:sPtr1、sPtr2、sPtr3,它们管理的对象分别为age = 0、age = 18、age = 22,打印了三行Person对象的构造函数中的输出sPtr4同时管理sPtr1管理的对象sPtr1被管理对象的所有权,此时因为sPtr4还在管理原sPtr1管理的对象age = 0,因此age = 0对象并没有被析构sPtr2被管理对象的所有权,此时因为age = 18对象没有任何shared_ptr进行管理,age = 18对象被析构,打印了age = 18对象的析构函数中的输出sPtr3被管理对象的所有权,sPtr3转为管理age = 19的对象;是先构造age = 19对象,后再释放sPtr3被管理对象的所有权,打印了age = 19对象的构造函数中的输出;此时因为sPtr3原管理的age = 22对象没有任何shared_ptr进行管理,age = 22对象被析构,打印了age = 22对象的析构函数中的输出Before return,管理age = 0和age = 19对象的智能指针sPtr4和sPtr3被析构,age = 0和age = 19对象没有智能指针管理,age = 0和age = 19对象被析构,打印了它们对象的析构函数中的输出。template< class Y >
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;
别名用于访问类的成员变量。
我们需要访问的是某个实例的成员,因此并不希望在使用这个成员的时候,对应的实例被销毁了。
使用别名的shared_ptr,增加了对这个实例的控制权,但仍然访问的是成员。
// ...
struct Person{ int age = 18; };
struct Student{ Person person; };
int main(){
shared_ptr<Student> studentPtr{make_shared<Student>()};
cout << "studentPtr use count = " << studentPtr.use_count() << endl;
shared_ptr<Person> personPtr{studentPtr, &(studentPtr->person)};
cout << "studentPtr use count = " << studentPtr.use_count() << endl;
cout << personPtr->age << endl;
return 0;
}
/** 输出:
studentPtr use count = 1
studentPtr use count = 2
18
**/
shared_ptr 与函数int main(){
auto func = [](shared_ptr<int> sPtr){
cout << "value = " << *sPtr << endl;
cout << "enter func: use count = " << sPtr.use_count() << endl;
};
auto sPtr = make_shared<int>(100);
cout << "init: use count = " << sPtr.use_count() << endl;
func(sPtr);
cout << "exit func: use count = " << sPtr.use_count() << endl;
return 0;
}
/** 输出:
init: use count = 1
value = 100
enter func: use count = 2
exit func: use count = 1
**/
按值传递,函数会复制一份参数,因此传入的sPtr会被复制一份,造成其对象的引用计数增加 1;
执行完这个函数,复制的那一份sPtr被销毁,使得其对象的引用计数减少 1;
int main(){
auto func = [](shared_ptr<int> &sPtr){
cout << "value = " << *sPtr << endl;
cout << "enter func: use count = " << sPtr.use_count() << endl;
};
auto sPtr = make_shared<int>(100);
cout << "init: use count = " << sPtr.use_count() << endl;
func(sPtr);
cout << "exit func: use count = " << sPtr.use_count() << endl;
return 0;
}
/** 输出:
init: use count = 1
value = 100
enter func: use count = 1
exit func: use count = 1
**/
按引用传递,函数不会复制一份参数;
因此若函数内部无其它导致增加引用计数的操作,函数执行过程中引用计数都不会改变。
constint main(){
auto func = [](const shared_ptr<int> &sPtr){
cout << "value = " << *sPtr << endl;
cout << "enter func: use count = " << sPtr.use_count() << endl;
sPtr.reset(); // error
sPtr.reset(new Person()); // error
sPtr.release(); // error
};
auto sPtr = make_shared<int>(100);
cout << "init: use count = " << sPtr.use_count() << endl;
func(sPtr);
cout << "exit func: use count = " << sPtr.use_count() << endl;
return 0;
}
使用const的引用传递,不能改变shared_ptr所管理的对象是哪一个,使用.reset()等都会造成编译错误。
shared_ptrint main(){
auto createSPtr = [](int i) -> shared_ptr<Person>{
shared_ptr<Person> sPtr = make_shared<Person>(i);
cout << "age = " << i << " use count = " << sPtr.use_count() << endl;
return sPtr;
};
shared_ptr<Person> sPtr = createSPtr(100);
sPtr->getAge(); cout << "use count = " << sPtr.use_count() << endl;
// 用作链式函数
createSPtr(200)->getAge();
cout << endl << "Before main return" << endl;
return 0;
}
/** 输出:
Constructor: person's age = 100
age = 100 use count = 1
Person's age = 100
use count = 1
Constructor: person's age = 200
age = 200 use count = 1
Person's age = 200
Destructor: person's age = 200
Before main return
Destructor: person's age = 100
**/
可见当用作链式函数时,使用完毕后,unique_ptr会被销毁,同时被管理的对象也被析构。
shared_ptr 的默认删除器不支持释放数组对象,需要指定删除器。
例,一维数组指定删除器
// ...
shared_ptr<int> ptr(new int[10], [](int* p) {delete[] p; });
同时,也可以使用 std::default_delete<T>() 函数作为删除器,这个函数内部的删除功能是通过delete释放,T 为释放什么类型的内存的类型。
例,一维数组指定删除器
// ...
shared_ptr<int> ptr(new int[10], default_delete<int[]>());
可以自己封装模板函数来使 shared_ptr 支持释放数组对象。
// ...
template <typename T>
shared_ptr<T> arrayShared_ptr(size_t size)
{
// 返回匿名对象
return shared_ptr<T>(new T[size], default_delete<T[]>());
}
int main()
{
shared_ptr<int> sPtr1 = arrayShared_ptr<int>(100);
shared_ptr<int> sPtr2 = arrayShared_ptr<char>(200);
return 0;
}
delete释放智能指针管理的对象的地址可以使用delete释放shared_ptr管理的对象的地址,但是其它共享指针仍然可能会访问这块地址,若访问了则会出现程序异常,因此应避免使用手动的delete。
// ...
shared_ptr<int> sPtr1 {new int(100)};
shared_ptr<int> sPtr2 = sPtr1;
delete sPtr1.get();
cout << *sPtr2 << endl; // error,运行时异常
如果一个地址内存,同时有原始指针和shared_ptr指向它,即使当所有shared_ptr都被销毁,原始指针还依然存在的情况下,这个地址内存仍然会被释放,若再用原始指针去访问这个内存地址就是访问了一块未知地址的内容。
// ...
int *i1 = new int{100};
shared_ptr<int> sPtr1{i1};
int *i2 = sPtr1.get();
cout << *i1 << endl;
cout << *i2 << endl << endl;
sPtr1.reset();
cout << *i1 << endl; // 危险行为
cout << *i2 << endl; // 危险行为
return 0;
/** 输出:
100
100
1664688 // 这是随机的,访问了一块未知地址的内容
1664688 // 这是随机的,访问了一块未知地址的内容
**/
同时,无论是使用shared_ptr,还是其它的智能指针,都应该避免与原始指针混用。
避免同时存在原始指针和智能指针的解决方案:
// ...
int *i = new int{100};
shared_ptr<int> sPtr1{i};
i = nullptr;
delete i;
cout << *sPtr1 << endl; // ok
shared_ptr不能使用一个原始指针初始化多个shared_ptr。
// ...
int *i = new int{100};
shared_ptr<int> sPtr1{i};
shared_ptr<int> sPtr2{i}; // error 编译通过 运行错误
shared_ptr 由于使用引用计数,因此会造成额外的内存和性能开销,因此在性能要求极为苛刻的情况下不适用。
unique_ptr是 0 开销的智能指针,也能够自动管理内存,但不会造成性能损失。
这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby类,但是我如何得到ActiveRecord关联这个类模型
我正在使用RubyonRails3.2.2,我想从我的模型/类中“提取”一些方法。也就是说,在不止一个类/模型中,我有一些方法(注意:方法与用户授权相关,并被命名为“CRUD方式”),这些方法实际上是相同的;所以我认为DRY方法是将这些方法放在“共享”模块或类似的东西中。实现该目标的常见且正确的方法是什么?例如,我应该将“共享”代码放在哪里(在哪些目录和文件中)?如何在我的类/模型中包含提到的方法?你有什么建议?注意:我正在寻找“RubyonRails制作东西的方式”。 最佳答案 一种流行的方法是使用ActiveSupport关注点
运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin
我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da
>>a=5=>5>>b=a=>5>>b=4=>4>>a=>5如何将“b”设置为实际的“a”,以便在示例中,变量a也将变为4。谢谢。 最佳答案 classRefdefinitializeval@val=valendattr_accessor:valdefto_s@val.to_sendenda=Ref.new(4)b=aputsa#=>4putsb#=>4a.val=5putsa#=>5putsb#=>5当您执行b=a时,b指向与a相同的对象(它们具有相同的object_id).当你执行a=some_other_thing时,a将指向
我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat
前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型
2022年底,OpenAI的预训练模型ChatGPT给人工智能领域的爱好者和研究人员留下了深刻的印象和启发,他展现的惊人能力将人工智能的研究和应用热度推向高潮,网上也充斥着和ChatGPT的各种聊天,他可以作诗、写小说、写代码、讨论疫情问题等。下面就是一些他的神回复:人命关天的坑: 写歌,留给词作者的机会不多了。。。 回答人类怎么样面对人工智能: 什么是ChatGPT?借用网上的一段介绍,ChatGPT是由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型,一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动
我目前有一个运行Ruby1.8.7和Rails2.3.2的RubyonRails项目我有一些从数据库中读取数据的单元测试,特别是两个连续项目的日期时间列,这两个项目应该相隔24小时。在一项测试中,我将项目2的日期时间设置为与项目1的日期时间相同。当我执行断言以确保两个值相等时,测试在rails2.3.2下工作正常。当我升级到rails2.3.11时,测试失败显示两次之间的差异将关闭并出现以下错误:expectedbutwas.这两个版本的rails中似乎存在浮点转换问题。如何解决float问题? 最佳答案 这也发生在我身上,我最终这
跳过联网激活:OOBE界面直接按Ctrl+Shift+F3进入审核模式。这样就可以直接进入系统进行一些硬件测试等,而不用联网激活导致新机无法退货。需要注意的是,在审核模式下进行的一些操作都会保留,并不会在退出后自动还原!安装的软件在正常开机进系统后还会看见!如果电脑确实没连互联网又不想强行跳过OOBE(网上很多教程会叫你直接结束OOBE进程,但这是不推荐的,因为一些厂商自带优化程序和系统初始化设置在后面都会应用,对于笔记本跳过的话你会发现驱动和内置应用都没有装上。其实这部分脚本就在系统盘的Recovery隐藏文件夹下),可以参考以下方式:https://www.landiannews.com/