当前位置: 首页 > news >正文

C++笔记---智能指针

1. 什么是智能指针

1.1 RALL设计思想

RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种资源管理类的设计思想,广泛应用于C++等支持对象导向编程的语言中。它的核心思想是将资源的管理与对象的生命周期紧密绑定,通过在对象的构造函数中获取资源,并在析构函数中释放资源,以此来确保资源总是被正确管理,避免资源泄露等问题。RAII利用了C++中对象自动调用析构函数的特性,即使在发生异常的情况下,也能保证资源被适时释放

1.2 智能指针的概念

智能指针是C++标准库中的一种高级指针管理工具,它们通过自动管理动态分配的内存来帮助预防内存泄漏和其他资源管理错误。

简单来说,智能指针是包装了指针的类,当智能指针对象的生命周期结束时,其析构函数会对指针指向的动态资源进行释放。

也就是说,我们将动态申请的内存交给智能指针对象来管理,将其的生命周期与智能指针的生命周期进行绑定,以此实现对动态分配的内存的自动释放。

智能指针相当于是给普通的指针加上了一个自动释放资源的功能,在行为上模拟普通指针的行为,重载了各种指针相关的操作符(operator*、operator->、operator[]等),因此叫做智能指针。

1.3 智能指针的应用场景

在C++中,智能指针的引入是为了简化和自动化内存管理,特别是在处理动态分配内存时,智能指针能够帮助防止常见的内存错误,如内存泄漏、悬挂指针和双重删除等。

在C++11之前,程序员需要手动管理动态分配的内存,这要求他们在适当的时机使用new和delete操作符来分配和释放内存。

这种管理方式不仅容易出错,而且在遇到异常时尤其难以保证资源被正确清理。

下面程序中我们可以看到,假如异常被抛出,就会导致array1和array2未被正常释放。

我们要处理这种情况,可以考虑先将异常捕获并将资源释放之后重新抛出,但这样的做法可维护性差,每此申请动态资源都需要再catch块中新增释放操作。除此之外,假如程序的其他部分抛出了异常(例如new也可能抛出异常),catch块中的释放操作也不会被执行。

double Divide(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Divide by zero condition!";} else{return (double)a / (double)b;}
}void Func()
{int* array1 = new int[10];int* array2 = new int[10];try{int len, time;cin >> len >> time;cout << Divide(len, time) << endl;} catch(...){cout << "delete []" << array1 << endl;cout << "delete []" << array2 << endl;delete[] array1;delete[] array2;throw; // 异常重新抛出,捕获到什么抛出什么} // ...cout << "delete []" << array1 << endl;delete[] array1;cout << "delete []" << array2 << endl;delete[] array2;
} int main()
{try{Func();} catch(const char* errmsg){cout << errmsg << endl;} catch(const exception & e){cout << e.what() << endl;} catch(...){cout << "未知异常" << endl;} return 0;
}

当引入智能指针之后,操作起来就要简单得多了:

template<class T>
class SmartPtr
{
public:// RAIISmartPtr(T* ptr): _ptr(ptr){}~SmartPtr(){cout << "delete[] " << _ptr << endl;delete[] _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t i){return _ptr[i];}
private:T* _ptr;
};double Divide(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Divide by zero condition!";} else{return (double)a / (double)b;}
}void Func()
{SmartPtr<int> array1(new int[10]);SmartPtr<int> array2(new int[10]);int len, time;cin >> len >> time;cout << Divide(len, time) << endl;
}int main()
{try{Func();} catch(const char* errmsg){cout << errmsg << endl;} catch(const exception & e){cout << e.what() << endl;} catch(...){cout << "未知异常" << endl;} return 0;
}


2. C++标准库中的智能指针

智能指针在设计时存在一个较大的难题:那就是拷贝时的行为如何设计?

假如我们直接将一个智能指针内部的指针拷贝给另一个智能指针,那么当两个智能指针的生命周期都结束时,同一个动态资源就会被释放两次。

针对这个问题,C++标准库中给出了几种不同的方案,这些智能指针都在<memory>这个头文件下面,我们包含<memory>就可以使用了。

其中weak_ptr比较特殊,它仅用于辅助shared_ptr处理循环引用的问题,我们在第3部分再解释。

2.1 std::auto_ptr

auto_ptr是C++98时设计出来的智能指针,他的特点是拷贝时把被拷贝对象的资源的管理权转移给拷贝对象,这是一个非常糟糕的设计,因为他会到被拷贝对象悬空,访问报错的问题,C++11设计出新的智能指针后,强烈建议不要使用auto_ptr

C++11出来之前很多公司也是明令禁止使用这个智能指针的。

个人感觉auto_ptr是最贴合设计初衷的名字,应该给下面行为最接近普通指针shared_ptr使用,但可惜好名字被狗用了。

实现示例:

namespace lbz
{template<class T>class auto_ptr{public:auto_ptr(T* ptr): _ptr(ptr){}auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){// 管理权转移sp._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){// 检测是否为⾃⼰给自己赋值if (this != &ap){// 释放当前对象中资源if (_ptr)delete _ptr;// 转移ap中资源到当前对象中_ptr = ap._ptr;ap._ptr = NULL;}return*this;}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}} // 像指针一样使用T & operator*(){return *_ptr;} T* operator->(){return _ptr;}private:T* _ptr;};
}

2.2 std::unique_ptr

std::unique_ptr是一种独占所有权的智能指针,它确保在任何时刻只有一个unique_ptr实例可以拥有和管理所指向的对象。一旦unique_ptr实例超出其作用域或被显式重置,它所管理的对象将被自动释放。

unique_ptr不支持拷贝操作,但可以通过移动语义来转让所有权。

实现示例
namespace lbz
{template<class T>class unique_ptr{public:explicit unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针⼀样使用T & operator*(){return *_ptr;} T* operator->(){return _ptr;} // 禁止直接拷贝unique_ptr(const unique_ptr<T>& sp) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;unique_ptr(unique_ptr<T>&& sp):_ptr(sp._ptr){sp._ptr = nullptr;} unique_ptr<T>& operator=(unique_ptr<T>&& sp){delete _ptr;_ptr = sp._ptr;sp._ptr = nullptr;}private:T* _ptr;};
}

2.3 std::shared_ptr

std::shared_ptr提供了共享所有权的智能指针,允许多个shared_ptr实例共享同一个对象的所有权。对象的生命周期由最后一个持有该对象的shared_ptr实例决定。当没有任何shared_ptr实例指向该对象时,对象会被自动释放。

shared_ptr是行为最贴近普通指针的智能指针,实践当中的使用最多,所以一般提到智能指针默认就是指shared_ptr。

shared_ptr的原理

shared_ptr内部使用引用计数来跟踪当前有多少个shared_ptr实例正在引用同一个对象。

为了实现这一点,我们引入了一个动态申请的int对象来进行引用计数(即下面代码中的_pcount)。

当直接使用指针进行构造时,申请一个int对象为其计数;当进行拷贝构造时,int对象++。

注意,这里不能使用静态成员变量来进行计数,因为静态成员变量统计的是shared_ptr的数量,而不是指向某一个资源的shared_ptr的数量。

删除器

智能指针析构时默认是进行delete释放资源,这也就意味着如果不是new出来的资源,交给智能指针管理,析构时就会崩溃。

智能指针支持在构造时给一个删除器,所谓删除器本质就是一个可调用对象,这个可调用对象中实现你想要的释放资源的方式,当构造智能指针时,给了定制的删除器,在智能指针析构时就会调用删除器去释放资源。

注意:unique_ptr的删除器通过在实例化模板时显式给出类型进行传递,shared_ptr只需要在构造函数的参数列表中给出可调用对象即可(lambda表达式使用更加方便)。

因为new[]经常使用,所以为了使用方便,unique_ptr和shared_ptr都特化了一份[]的版本:

unique_ptr<Date[]> up1(new Date[5]); 
shared_ptr<Date[]> sp1(new Date[5]); 

在释放资源时,会用delete[]来进行释放。 

其他注意事项

1. shared_ptr 除了支持用指向资源的指针构造,还支持 make_shared 用初始化资源对象的值直接构造:

template <class T, class... Args> 
shared_ptr<T> make_shared(Args&&... args);

 2. shared_ptr 和 unique_ptr 都支持了operator bool的类型转换,如果智能指针对象是一个空对象没有管理资源,则返回false,否则返回true,意味着我们可以直接把智能指针对象给if判断是否为空。

3. shared_ptr 和 unique_ptr 的构造函数都使用了explicit 修饰,以防止普通指针隐式类型转换成智能指针对象。

实现示例

需要注意的是我们这里实现的shared_ptr和weak_ptr都是以最简洁的方式实现的,只能满足基本的功能,这里的weak_ptr.lock等功能是无法实现的,想要实现就要把shared_ptr和weak_ptr一起改了,把引用计数拿出来放到一个单独类型,shared_ptr和weak_ptr都要存储指向这个类的对象才能实现,有兴趣可以去翻翻源代码。

namespace lbz
{template<class T>class shared_ptr{public:shared_ptr(T* ptr, function<void(T*)> del = ([](T* ptr) {delete ptr; })):_ptr(ptr), _pcount(new int(1)), _del(del){}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _del(sp._del){(*_pcount)++;}void release(){if (--(*_pcount) == 0){delete _pcount;_del(_ptr);_pcount = nullptr;_ptr = nullptr;}}~shared_ptr(){release();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){// 只剩一个,且自己给自己赋值时必须这样解决if (_ptr != sp._ptr){release();_ptr = sp._ptr;_pcount = sp._pcount;_del = sp._del;++(*_pcount);}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t i){return _ptr[i];}T* get() const{return _ptr;}int use_count() const{return *_pcount;}private:T* _ptr;int* _pcount;function<void(T*)> _del;};
}

3. 循环引用

假如我们在双向链表中使用shared_ptr来管理资源会发生什么?

#include<memory>
using namespace std;struct ListNode
{ListNode(int val):_val(val){}int _val;std::shared_ptr<ListNode> _prev = nullptr;std::shared_ptr<ListNode> _next = nullptr;
};int main()
{std::shared_ptr<ListNode> n1(new ListNode(10));std::shared_ptr<ListNode> n2(new ListNode(20));n1->_next = n2;n2->_prev = n1;return 0;
}

n1结点不仅被n1所指向,也被n2结点的prev所指向,引用计数为2,n2同理。

当程序结束时,n1、n2的生命周期结束,对应结点的引用计数各自减一,但此时两个结点仍各有1的引用计数,不会被正常释放。

只有当n2被释放,n2->prev的生命周期结束,n1才会被释放;但n2要被释放就需要n1先被释放,使n1->next的生命周期结束。

这样就导致两个结点互相引用、相互依存,始终不会被释放。

为了解决这个问题,就引入了weak_ptr来辅助shared_ptr的使用,weak_ptr相比于shared_ptr,其不会增加被指向资源的引用计数,也不负责资源的释放,将prev和next替换为weak_ptr就可以避免循环引用。

std::weak_ptr

std::weak_ptr是一种辅助智能指针,它与shared_ptr一起使用,用于避免循环引用问题。

相比于shared_ptr,weak_ptr不增加对象的引用计数,因此它不会延长对象的生命周期。

weak_ptr不支持RAII,也不支持访问资源,所以我们看文档发现weak_ptr构造时不支持绑定到资源,只支持绑定到shared_ptr,绑定到shared_ptr时,不增加shared_ptr的引用计数,那么就可以解决上述的循环引用问题。

weak_ptr也没有重载operator* 和operator->等,因为他不参与资源管理,那么如果他绑定的shared_ptr已经释放了资源,那么他去访问资源就是很危险的。weak_ptr支持expired检查指向的资源是否过期,use_count也可获取shared_ptr的引用计数,weak_ptr想访问资源时,可以调用lock返回一个管理资源的shared_ptr,如果资源已经被释放,返回的shared_ptr是一个空对象,如果资源没有释放,则通过返回的shared_ptr访问资源是安全的。

namespace lbz
{template<class T>class weak_ptr{public:weak_ptr(){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.get()){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}private:T * _ptr = nullptr;};
} 

4. 智能指针使用建议

  • 优先使用unique_ptr来管理单个对象的所有权,因为它通常比shared_ptr更轻量级且更不容易产生错误。
  • 使用shared_ptr来管理共享所有权的资源,尤其是在多个对象或函数需要访问同一个资源的情况下。
  • 使用weak_ptr来避免与shared_ptr相关的循环引用问题。
  • 避免在全局作用域或静态存储持续期间使用智能指针,因为这可能会导致不必要的复杂性和潜在的资源管理问题。
  • 在可能发生异常的代码路径中使用智能指针,以确保即使在异常发生时资源也能被正确释放。
  • 当向容器或算法传递所有权时,使用std::move来将unique_ptr转换为右值引用,以便容器或算法可以接管所有权。

5. shared_ptr的线程安全问题

shared_ptr的引用计数对象在堆上,如果多个shared_ptr对象在多个线程中,进行shared_ptr的拷贝析构时会访问修改引用计数,就会存在线程安全问题,所以shared_ptr引用计数是需要加锁或者原子操作保证线程安全的。

shared_ptr指向的对象也是有线程安全的问题的,但是这个对象的线程安全问题不归shared_ptr管,它也管不了,应该有外层使用shared_ptr的人进行线程安全的控制。

下面的程序会崩溃或者A资源没释放,bit::shared_ptr引用计数从int*改成atomic<int>*就可以保证引用计数的线程安全问题,或者使用互斥锁加锁也可以。

struct AA
{int _a1 = 0;int _a2 = 0;~AA(){cout << "~AA()" << endl;}
};int main()
{bit::shared_ptr<AA> p(new AA);const size_t n = 100000;mutex mtx;auto func = [&](){for (size_t i = 0; i < n; ++i){// 这里智能指针拷贝会++计数bit::shared_ptr<AA> copy(p);{unique_lock<mutex> lk(mtx);copy->_a1++;copy->_a2++;}}};thread t1(func);thread t2(func);t1.join();t2.join();cout << p->_a1 << endl;cout << p->_a2 << endl;cout << p.use_count() << endl;return 0;
}

6. C++11和boost中智能指针的关系

Boost库是为C++语言标准库提供扩展的一些C++程序库的总称,Boost社区建立的初衷之一就是为C++的标准化工作提供可供参考的实现,Boost社区的发起人Dawes本人就是C++标准委员会的成员之一。

在Boost库的开发中,Boost社区也在这个方向上取得了丰硕的成果,C++11及之后的新语法和库有很多都是从Boost中来的。

• C++ 98 中产生了第一个智能指针auto_ptr;
• C++ boost给出了更实用的scoped_ptr / scoped_array和shared_ptr / shared_array和

  weak_ptr等;
• C++ TR1,引入了shared_ptr等,不过注意的是TR1并不是标准版;
• C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。

需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。

7. 内存泄露

7.1 内存泄露的定义和危害

内存泄露是指程序在运行过程中动态分配的内存未能被正确释放或回收,导致这部分内存无法供其他程序或同一程序的其他部分使用。随着时间的推移,未被释放的内存会被不断累积,可能导致系统可用内存减少,进而使程序运行变慢、响应时间增加,甚至可能引发系统崩溃或资源耗尽。

7.2 如何检测内存泄露

检测内存泄露通常需要使用专门的工具,这些工具可以在程序运行时分析内存的分配和释放情况。常见的内存泄露检测工具包括Valgrind、AddressSanitizer、Visual Studio的内存诊断工具等。这些工具能够帮助开发人员识别不被释放的内存分配,并定位到代码中的具体位置。

7.3 如何避免内存泄露

避免内存泄露的关键在于遵循良好的编程习惯和系统设计原则:

  1. 严格管理内存分配和释放,确保每次动态内存分配后都有相应的释放操作。
  2. 使用智能指针和内存池等技术来自动管理内存,减少手动内存管理的错误。
  3. 进行定期的代码审查,以发现潜在的内存泄露风险点。
  4. 编写单元测试,确保内存管理的正确性。
  5. 对于不熟悉的库或框架,仔细阅读文档,了解其内存管理的细节。

相关文章:

C++笔记---智能指针

1. 什么是智能指针 1.1 RALL设计思想 RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;资源获取即初始化&#xff09;是一种资源管理类的设计思想&#xff0c;广泛应用于C等支持对象导向编程的语言中。它的核心思想是将资源的管理与对象的生命周期紧密绑定…...

CentOS 7系统中更改YUM源为阿里云的镜像源

引言 更换阿里的镜像源可以带来诸多好处&#xff0c;包括提高下载速度、提升稳定性、同步更新、简化配置、节省带宽资源以及增强系统安全性等。因此&#xff0c;对于使用CentOS系统的用户来说&#xff0c;更换阿里的镜像源是一个值得考虑的选择。 1.备份yum源 mv /etc/yum.r…...

Python酷库之旅-第三方库Pandas(206)

目录 一、用法精讲 961、pandas.IntervalIndex.mid属性 961-1、语法 961-2、参数 961-3、功能 961-4、返回值 961-5、说明 961-6、用法 961-6-1、数据准备 961-6-2、代码示例 961-6-3、结果输出 962、pandas.IntervalIndex.length属性 962-1、语法 962-2、参数 …...

3.4CQU数学实验???

meshgrid 是一个用于生成网格点坐标的函数。它常用于在二维或三维空间中创建坐标网格&#xff0c;用于可视化和数据处理。 在二维情况下&#xff0c;meshgrid 函数接受两个一维数组作为输入&#xff0c;并返回两个二维数组&#xff0c;这两个数组中的元素分别表示了所有可能的…...

Linux(CentOS)开放端口/关闭端口

一、普通用户使用 sudo 操作&#xff0c;开放/关闭端口&#xff0c;80 1、检查端口是否开放 sudo firewall-cmd --zonepublic --query-port80/tcp 2、开放端口 sudo firewall-cmd --zonepublic --add-port80/tcp --permanent 3、重新加载&#xff08;开放或关闭端口后都需…...

GreenDao适配AGP8.7+

升级配置 工具版本Android StudioLadybug 2024.2.1 Path2AGP8.7.2KPG1.8.21GGP3.3.1明细 classpath "com.android.tools.build:gradle:$agp_version"classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kgp_version"classpath "org.greenrobot:g…...

【前端】Typescript从入门到进阶

以下是 TypeScript 的常用知识点总结&#xff0c;涵盖了从基础到入门的内容&#xff0c;并配有代码示例&#xff1a; 1. TypeScript 基础 1.1 安装和配置 安装 TypeScript 并初始化配置文件&#xff1a; npm install -g typescript tsc --init 1.2 基本类型 TypeScript 提供…...

在 RHEL 8 | CentOS Linux release 8.5.2111上安装 Zabbix 6

1. 备份YUM源文件 cd /etc/yum.repos.d/ mkdir bak mv C* ./bak/ wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo yum clean all yum makecache2. 将 SELinux 设置为宽容模式&#xff0c;如下所示。 sudo s…...

光纤HDMI线怎么连接回音壁?

第一步&#xff1a;准备HDMI线、光纤线&#xff08;TOSLINK线&#xff09;、视频源设备、回音壁 第二步&#xff1a;连接HDMI线&#xff0c;找到视频源设备上的HDMI输出口&#xff0c;将HDMI线的一端插入这个接口&#xff0c;再把HDMI线的另一端插入回音壁的HDMI输入口。注意检…...

屏幕后期处理

1、屏幕后期处理效果 屏幕后期处理效果&#xff08; Screen Post-Processing Effects&#xff09;是一种在渲染管线的最后阶段应用的视觉效果&#xff0c;允许在场景渲染完成后对最终图像进行各种调整和效果处理&#xff0c;从而增强视觉体验 常见的屏幕后期处理效果有&#x…...

K8资源之endpoint资源EP资源

1 endpoint资源概述 endpoint资源在K8S中用来表s示vc与后端 Pod 之间的连接关系的对象。当创建svc时&#xff0c;svc根据标签是否相同或svc名字是否和ep名字相同&#xff0c;把svc和ip关联上。 删除svc时&#xff0c;会自动的删除同名的ep资源。 2 ep资源和svc的关联测试 […...

微软日志丢失事件敲响安全警钟

NEWS | 事件回顾 最近&#xff0c;全球最大的软件公司之一——微软&#xff0c;遭遇了一场罕见的日志丢失危机。据报告&#xff0c;从9月2日至9月19日&#xff0c;持续长达两周的时间里&#xff0c;微软的多项核心云服务&#xff0c;包括身份验证平台Microsoft Entra、安全信息…...

Qt生成应用程序exe

1. 将工程用MinGW编译器在release模式下编译&#xff0c;生成可执行文件XXX.exe&#xff0c;新建一个文件夹如&#xff1a;F:\Setup\minGW&#xff0c;把exe文件放到这个目录下。 2. 将该编译器的bin文件添加到PATH环境变量里&#xff1a;bin文件路径为&#xff1a;D:\Qt\Qt5.…...

C#中的HttpContent、HttpClientHandle、HttpWebRequest

C#中的HttpContent 在C#中&#xff0c;HttpContent 是 System.Net.Http 命名空间下的一个类&#xff0c;它是 HttpClient 类用来发送和接收HTTP内容的基础。HttpContent 表示HTTP请求或响应的正文内容&#xff0c;并且可以序列化和反序列化数据。 HttpContent 是一个抽象类&a…...

23.网工入门篇--------介绍一下园区网典型组网架构及案例实践

园区网典型组网架构主要分为小型、中型、大型三种类型&#xff0c;以下是详细介绍及相关案例实践&#xff1a; 小型园区网&#xff1a; 架构特点&#xff1a; 用户规模&#xff1a;适用于接入用户数量较少的场景&#xff0c;一般支持几个至几十个用户。覆盖范围&#xff1a;仅限…...

QT鼠标事件

QT鼠标事件 1.概述 这篇文章介绍如何使用事件和获取事件的信号 2.创建项目 创建一个widget类型项目&#xff0c;在widget.ui文件中添加一个label控件 然后在项目名称上右键选择Add new... 添加文件&#xff0c;选择 C Class 自定义类名Mylabel&#xff0c;选择基类Base …...

Ubuntu 的 ROS 操作系统turtlebot3环境搭建

引言 本文介绍如何在Ubuntu系统中为TurtleBot3配置ROS环境&#xff0c;包括安装和配置ROS Noetic的步骤&#xff0c;为PC端控制TurtleBot3提供操作指南。 安装和配置的过程分为PC设置、系统安装、依赖安装等部分&#xff0c;并在最后进行网络配置&#xff0c;确保PC端能够顺利…...

C++笔记---异常

1. 异常的概念 1.1 异常和错误 异常通常是指在程序运行中动态出现的非正常情况&#xff0c;这些情况往往是可以预见并可以在不停止程序的情况下动态地进行处理的。 错误通常是指那些会导致程序终止的&#xff0c;无法动态处理的非正常情况。例如&#xff0c;越界访问、栈溢出…...

Python 操作数据库:读取 Clickhouse 数据存入csv文件

import pandas as pd from clickhouse_driver import Client import timeit import logging import threading from threading import Lock from queue import Queue from typing import List, Dict, Set from contextlib import contextmanager import os import time# 配置参…...

如何找到系统中bert-base-uncased默认安装位置

问题&#xff1a; 服务器中无法连接huggingface&#xff0c;故需要自己将模型文件上传 ubuntu 可以按照这个链接下载 Bert下载和使用&#xff08;以bert-base-uncased为例&#xff09; - 会自愈的哈士奇 - 博客园 里面提供了giehub里面的链接 GitHub - google-research/be…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西&#xff0c;但是如果把三者放在一起&#xff0c;它们之间到底什么关系&#xff1f;又有什么联系呢&#xff1f;我不是很明白&#xff01;&#xff01;&#xff01; 就比如说&#xff1a; 沙箱&#…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...