C++进阶--C++11--智能指针(重点)
文章目录
- C++进阶--智能指针(重点)
- 智能指针使用的场景
- RAII和智能指针的设计思路
- C++标准库智能指针的使用
- 定制删除器
- 智能指针的原理
- shared_ptr和weak_ptr
- 循环引用(容易考)
- weak_ptr
- 其他
- 知识扩展(类型转换)
- 总结
- 个人学习心得
- 结语
很高兴和大家见面,给生活加点impetus!!开启今天的编程之路!!
今天我们进一步c++11中常见的新增表达
作者:٩( ‘ω’ )و260
我的专栏:Linux,C++进阶,C++初阶,数据结构初阶,题海探骊,c语言
欢迎点赞,关注!!
C++进阶–智能指针(重点)
智能指针使用的场景
前面我们学习了异常的相关知识,明白了异常的使用语法,异常的底层原理,学习了异常的一些细节,但是我们还有一种情况,使用异常代码可视化非常低,而且代码也不好看。
来看示例:
void Divide(int a,int b)
{if(b == 0){throw string("error: Divided by zero!");}else {cout << double(a)/double(b) << endl;}
}
int main()
{int arr1 = new int[10];int arr2 = new int[10];try{int a,b;cin >> a >> b;Divide(a,b);}catch(...)//捕获任意类型的错误{//相关操作,释放arr1,arr2}return 0;
}
我们下方写catch(…)的目的就是为了来释放前面申请到的arr1和arr2资源。
但是new本身可能抛异常啊,如果说此时申请arr1抛异常,无伤大雅,因为资源还没有申请下来,但是如果是申请arr2抛异常,那么我的arr1肯定是已经申请下来了,不然走不到申请arr2资源的这句代码中去。
所以:如果使用异常解决这个问题的话,需要在申请arr2资源的时候再来套一个try,catch,缺点就是代码可视化降低
我们来尝试使用智能指针解决这个问题
RAII和智能指针的设计思路
首先:这里的问题就是需要析构的东西抛异常可能会导致无法执行正确的析构,进而导致内存泄漏。
如果说我们将申请到的资源托管给一个对象,那就可以解决这个问题,为什么呢?因为对象不论是局部还是全局的,函数执行结束或者代码执行结束这个对象会调用自己的析构,即:对象定义和销毁的时候构造函数和析构函数是不用用户自己调用的,编译器自己帮我们调用
RALL(资源申请立即初始化):本质是⼀种利用对象生命周期来管理获取到的动态资源,避免内存泄漏。RAII在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
智能指针除了需要满足RALL思想,还需要设计一系列接口还管理这个资源
我们可以写一个简单的项目来描述智能指针需要设计的内容:
namespace Mrzeng
{template<class T>//可能接收到各种类型的指针class Smart_ptr{public:Smart_ptr(T* ptr):_ptr(ptr){}~Smart_ptr(){delete _ptr;//对象调用析构的时候将申请到的资源释放掉}//一系列简单接口T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get(){return _ptr;}public:T* _ptr;}
}
反正记住:operator->一定是返回一个地址,因为到时候使用的时候实际上有两个->,前者是operator->(),后者单纯是->操作符。->操作符对指针操作,所以operator->()必须返回一个地址。
学习了RALL思想,上述代码应该这样修改
int main()
{Mrzeng::Smart_ptr<int> sp1(new int[10]);//这种情况下,不会存在内存泄漏的问题Mrzeng::Smart_ptr<int> sp2(new int[10]);//这种情况下,不会存在内存泄漏的问题try{int a,b;cin >> a >> b;Divide(a,b);}catch(...)//捕获任意类型的错误{//相关操作,释放arr1,arr2}return 0;
}
C++标准库智能指针的使用
头文件:< memory >
智能指针主要分为两种,在此之前,我们先介绍c++98中的智能指针。
auto_ptr:
首先先说明结论:auto_ptr在很多企业中的使用条例被禁止使用,原因为底层实际上是管理权转移。
我们主要使用日期类来演示,来看代码:
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){}~Date(){cout << "~Date" << endl;}
private:int _year = 1;int _month = 1;int _day = 1;
};int main()
{auto_ptr<Date> ap1(new Date(2025, 6, 8));auto_ptr<Date> ap2(ap1);//使用ap1拷贝ap2cout << ap2.get() << endl;cout << ap1.get() << endl;return 0;
}
我们发现,这个时候ap1(被拷贝对象)资源全部清空了,本质上就是ap2(拷贝对象)拷贝之后内部重置了ap1,造成ap1的悬空
正是因为这个原因,而且ap1的悬空还是内部自己实现的,造成很多程序员书写的时候可能不会注意到这点。
c++11中的智能指针:unique_ptr和shared_ptr
unique_ptr:特点是只能够对单一资源进行管理,即1对1,。所以内部没有实现拷贝构造,只有移动构造,移动构造是对资源进行转移,肯定能够保证1对1的规则
我们发现:为什么unique_ptr和auto_ptr效果一样,可后者被禁用了呢?因为后者是底层搞得,上层使用者没学习的话根本不知道,但是前后调用移动构造,学习了右值引用章节知识肯定知道也不会再对资源进行访问
shared_ptr:特点是可以多个shared_ptr管理一个资源,即多对1,也可以一个shared_ptr管理一个资源,即1对1。内部实现了拷贝构造。既然可以由多个对象管理一块资源,为了防止一块资源被delete多次(同一块资源被delete多次,直接报错),使用了引用计数的方法。引用计数的目的就是防止同一块空间被析构多次
shared_ptr和unique_ptr区别:前者可以拷贝构造,后者不能够拷贝构造。
思考:shared_ptr能够管理一块资源,即1对1,unique_ptr也能够管理一块资源,即1对1,为什么还要有unique_ptr呢?
shared_ptr引用计数底层有性能消耗,所以如果管理对象一不涉及拷贝,更适合使用unique_ptr。反之更适合使用shared_ptr。
c++第三个智能指针:weak_ptr
特点:不符合RALL规则,所以不能够来使用管理对象。weak_ptr的产生是为了解决shared_ptr循环引用的问题,这点我们稍后讲解
定制删除器
我们再来看,前面我们举例都是使用delete删除资源的,确实,在标准库中,智能指针的默认析构资源使用的是delete,如果我是申请的数组呢?是不是需要使用delete [ ]呢?但是后面c++更新中模版特化了这种情况
那如果申请的资源是fopen使用的文件的,释放这个资源应该使用fclose而非delete。
为了处理这种情况,需要定制删除器。
删除器的本质就是一个可调用对象。
这里设计的复杂了,同样都是只能指针,但是传递删除器的方式却不相同。
先来看如何使用:
shared_ptr:
因为是函数模版,所以需要在构造shared_ptr的时候传递一个可调用对象过去:
struct FClose
{void operator()(FILE* ptr){cout << "fclose:ptt" << endl;fclose(ptr);}
};int main()
{//使用仿函数执行删除器shared_ptr<FILE> sp1(fopen("Test1.cpp", "r"),FClose());//需要传一个对象过去,是传给的函数模版,函数指针和仿函数相差不大,这里不再演示shared_ptr<FILE> sp2(fopen("Test2.cpp", "r"), [](FILE* ptr) {//传lambda表达式过去cout << "fclose:ptr" << endl;fclose(ptr);});return 0;
}
unique_ptr
因为删除器是作为模版参数传过去的,所以传递的应该是一个类型
struct FClose
{void operator()(FILE* ptr){cout << "fclose:ptt" << endl;fclose(ptr);}
};int main()
{//使用仿函数执行删除器unique_ptr<FILE,FClose> sp1(fopen("Test1.cpp", "r"));//unique_ptr删除器是在模版位置传递的,所以传一个类型,函数指针与仿函数相差不大,不在演示//演示传递lambda表达式,比较复杂,先来看结果auto FClose = [](FILE* ptr) {cout << "fclose:ptr" << endl;fclose(ptr);};unique_ptr<FILE, decltype(FClose)> sp2(fopen("Test1.cpp", "r"),FClose);return 0;
}
解释:decltype(类型推导关键字):在编译时进行类型推导。
前面已经提到,unique_ptr的定制删除器需要使用类型传递给模版,但是lambda表达式是一个匿名函数对象,我传递的需要是类型,所以使用decltype关键字。
其次:后面为什么要传一个FClose呢?
因为lambda表达式无法实例化出对象,需要使用拷贝构造拷贝出一个对象,调用这个对象,执行完删除资源的操作之后就需要将这个对象析构。不可能我去调用一个类型吧。
其实,调用了这个unique_ptr中的构造函数的这个接口:
记住:如果头铁就要使用unique_ptr来定制删除器,如果是lambda表达式作为模版参数的话,记得构造时添加一个可调用对象,左值或右值都可以
总结:定制删除器尽量都是用shared_ptr,而且尽量都是用lambda表达式
细节:上述定制删除器unique_ptr因为传的是模版,所以在整个类中都是可以直接使用这个对象的。但是,shared_ptr中发现,定制删除器是作为一个函数模版有函数模版推导而来的。
那么我们如果在需要析构资源的时候使用到这个定制删除器呢?
使用包装器,目的是将删除器保存下来,想用的时候就用,而不限于只有构造函数能够用这个删除器。而且,最好包装器需要给一个缺省值走初始化列表,因为包装器(这里使用function)的默认构造是空的,但是去取的话,如果包装器是空,就会抛异常,这里跟库一样,将delete设置为缺省值,还要注意包装器的类型,因为什么事情也不干,返回值是void,传递的参数是T*
智能指针的原理
1:首先需要包含定制删除器的细节
2:析构函数的细节:
只有当引用计数为1的时候,我们才需要析构这块资源,否则只是引用计数减少1
3:拷贝构造的细节:
前面我们提到unique_ptr不支持拷贝构造,只支持移动构造,所以,如果我们创建一个对象并初始化调用构造函数只能够这样写:
shared_ptr<Date> sp1(new Date(2025,6,8));//能够这样写
shared_ptr<Date> sp1 = {2025,6,8};//不能够这样写。
想一下问什么不能够这样写呢?
这需要了解类型转换相关的知识,这点我会在后面讲解。
首先我们使用sp1来管理资源为{2025,6,8}的对象。
会执行的操作如下:构造+拷贝构造(会被直接优化成直接构造)
问题:构造+拷贝构造优化为直接构造在c++11中c++委员会规定还没有规定!即有些编译器优化了,有些编译器没有优化。但是unique_ptr是不支持拷贝构造的,即有些编译器会出错,有些编译器可能不会报错!
为了杜绝这种情况发生,使用了explicit修饰,目的是:避免类的单参数构造函数被用于隐式类型转换
其实这里还有一种写法,使用make_shared+传递参数返回一个shared_ptr,作用是:它能够减少代码冗余,用户不用显式地书写 new 表达式,局限是:没办法自定义删除器(deleter)。如果确实需要自定义删除器,那就得直接使用 new 来创建 std::shared_ptr(依靠构造函数)
4:赋值重载细节:注意防止地址重复赋值和相同地址的赋值
来看案例:sp1 = sp2
将sp2赋值给sp1,需要考虑sp1的引用计数是否为1,如果为1,则直接析构,然后修改为sp2,如果sp1的引用计数不为1,只用修改sp1管理资源的引用计数。然后修改成sp2,最后sp2的引用计数需要加1,因为多了一个sp1来管sp2管的资源了。
而且:如果说sp1和sp2地址相同,就啥也不做,做了也是白做。
最后代码部分已经绑定到这篇文章了。
shared_ptr和weak_ptr
循环引用(容易考)
shared_ptr看似非常完美,既支持拷贝构造,也能多个shared_ptr管理一个资源。但是还有不足,来看下面案例:
如果此时需要创建一个链表,这个链表中只有两个结点,这两个结点相互指向,同时,我还需要智能指针来管理这两个结点。
来看代码:
struct ListNode
{int _data;/*ListNode* _next;ListNode* _prev;*/std::shared_ptr<ListNode> _next;std::shared_ptr<ListNode> _prev;~ListNode(){cout << "~ListNode()" << endl;}
};
int main()
{std::shared_ptr<ListNode> n1(new ListNode);std::shared_ptr<ListNode> n2(new ListNode);cout << n1.use_count() << endl;cout << n2.use_count() << endl;n1->_next = n2;n2->_prev = n1;cout << n1.use_count() << endl;cout << n2.use_count() << endl;return 0;
}
先说明一下为什么_next指针的类型是这个,下方我们使用了share_ptr来管理结点,可以理解为此时这个结点的类型是一个智能指针了,只不过真正的结点被这个share_ptr管理着。所以此时_next和_prev都给换了一个类型。不换的话是会报错的-类型不匹配。
结果:
代码执行结束,管理的对象销毁,但是,并没有调用析构函数,违背了RALL规则,为什么呢?
来看图示:
先明白,为什么引用计数变成2了?
因为_next和_prev指针都是一个智能指针,管理一个资源之后这个资源引用计数肯定需要加1
循环引用的逻辑:左边结点引用计数为1,想要销毁,必须让管理左结点的对象prev销毁,对象prev在右边结点中,右边结点销毁,其中的内容销毁,右边结点想要销毁,必须让管理右边结点的对象next销毁,但是next在左边结点,左边结点销毁,其中的内容销毁,要想让左边结点销毁,必须让管理左边结点的对象prev销毁。我们发现,是不是回到了第一句!!即左结点想要销毁,左结点才能够销毁!!
并且,如果不是相互指向,都不会右循环引用,即单个方向指向不存在循环引用。
再来想一下是什么原因造成这一个现象的?
原因是不是next和prev指针增加了对应资源的引用计数!!
所以,为了不增加对应资源的引用计数,同时还要链接两个节点,weak_ptr运应而生。
weak_ptr不遵循RALL规则,不管理资源,也就不会增加引用计数
上述代码只用将_next和_prev的类型修改为weak_ptr< ListNode >,就能够杜绝循环引用的现象
来看结果:
析构正常执行!!
weak_ptr
weak_ptr主要解决shared_ptr的循环引用的问题,不遵循RALL,不会增加引用计数,相应的,其中没有operator*和operator->等接口,因为weak_ptr不管理资源。因为如果weak_ptr绑定的shared_ptr已经释放了资源,那么weak_ptr再去访问资源十分危险。
weak_ptr支持expired检查指向的资源是否过期,use_count也可获取shared_ptr的引用计数,weak_ptr想访问资源时,可以调用lock返回一个管理资源的shared_ptr,如果资源已经被释放,返回的shared_ptr是一个空对象,如果资源没有释放,则通过返回的shared_ptr访问资源是安全的。
是否过期的判断条件 :shared_ptr对象管理的资源,如果这些对象都不管理这个资源了,就过期,反之只要还有一个对象管理着这个资源,就没有过期,呈现的结果为为真即过期,为假不过期。
来看示例代码:
int main()
{std::shared_ptr<string> sp1(new string("111111"));std::shared_ptr<string> sp2(sp1);std::weak_ptr<string> wp = sp1;cout << wp.expired() << endl;cout << wp.use_count() << endl;// sp1和sp2都指向了其他资源,则weak_ptr就过期了sp1 = make_shared<string>("222222");cout << wp.expired() << endl;cout << wp.use_count() << endl;sp2 = make_shared<string>("333333");cout << wp.expired() << endl;cout << wp.use_count() << endl;wp = sp1;//std::shared_ptr<string> sp3 = wp.lock();auto sp3 = wp.lock();//会返回一个shared_ptrcout << wp.expired() << endl;cout << wp.use_count() << endl;*sp3 += "###";cout << *sp1 << endl;return 0;
}
来看结果:
其他
剩下的还有线程安全问题,之后我们再讲解。
知识扩展(类型转换)
上面我们提到了类型转换,我们可以浅浅讲解一点。
这里直接引出结论:
其他类型a(自定义类型,内置类型)转换成自定义类型b:支持自定义类型的构造函数可以使用a作为参数,然后分为单参数和多参数的区别。
自定义类型a转换成内置类型:使用重载函数,operator 内置类型(参数),返回类型为该内置类型
为什么是这样呢?首先强制类型转换操作符是(),如int a = 1;(double)a;
但是()已经被仿函数占用了,所以只能够定义成如此了。
来看示例:
场景:判断sp1是否管理了资源,可以使用
if(sp1)
{//操作
}
//等价于
if(sp1.operator bool())
{//操作
}
当然在流插入中也有operator bool()也有所涉及,使用场景如下
while(cin >> x)
{//...
}
总结
今天我们学习了智能指针,分析了使用智能指针的场景,智能指针满足的规则,底层,定制删除器(语法,总结规律,什么场景下需要使用),循环引用(重点),以及如何来手撕unique_ptr,shared_ptr,weak_ptr这三种智能指针和这三类智能指针的特点区别,学习了类型转换和一些细节。
这些知识点记得复习哦
个人学习心得
在c++11之后,走隐式类型转换的途径一般都是通过给的形参形成一个initializer_list,看这个initializer_list能不能来初始化我想形成的类。
例如有一个日期类:
shared_ptr<Date> sp1 = {2025,6,8};//不能够这样写。
如果说此时我的日期类里面书写了initializer_list这个构造方式,就能够来构造一个Date,并且此时拷贝构造没有explicit关键字修饰(但是库中是有这个修饰的哈),就能够转换为一个Date*
结语
感谢大家阅读我的博客,不足之处欢迎留言指出,感谢大家支持!!
路漫漫其修远兮,吾将上下而求索
相关文章:

C++进阶--C++11--智能指针(重点)
文章目录 C进阶--智能指针(重点)智能指针使用的场景RAII和智能指针的设计思路C标准库智能指针的使用定制删除器智能指针的原理shared_ptr和weak_ptr循环引用(容易考)weak_ptr 其他知识扩展(类型转换)总结个人学习心得结语 很高兴和…...
CSP-38th
目录 1.正态分布 2.走马 3.信息传输 4.字符串可能性个数 5.最多访问节点个数 1.正态分布 本来是很简单的一道模拟题,根据 (n-u) /a 的整数位、十分位确定是在第几行,根据百分位确定是在第几列,但是我直接将 (n-u)/a 乘以100后进行 // 和…...

企业私有化部署DeepSeek实战指南:从硬件选型到安全运维——基于国产大模型的安全可控落地实践
一、部署前的战略评估与规划 私有化部署不仅是技术工程,更是企业数据战略的核心环节。需重点评估三方面: 1、业务场景适配性 适用场景:金融风控(需实时数据处理)、医疗诊断(敏感病历保护)、政…...

【西门子杯工业嵌入式-5-串口实现数据收发】
西门子杯工业嵌入式-5-串口实现数据收发 一、通信基础1.1 什么是通信1.2 嵌入式系统中的通信 二、串行通信原理2.1 串行通信简介2.2 通信参数约定 三、GD32F470 串口资源与性能3.1 串口硬件资源 四、串口通信的实现4.1 串口初始化流程4.2 串口发送函数编写4.3 使用 printf 实现…...
F(x,y)= 0 隐函数 微分法
🟦 一、隐函数微分法简介 ▶ 什么是隐函数? 显函数:形如 y f ( x ) y f(x) yf(x),变量之间是显式关系。 隐函数:形如 F ( x , y ) 0 F(x, y) 0 F(x,y)0,变量间不是直接表达的,需要通过…...

深度学习登上Nature子刊!特征选择创新思路
2025深度学习发论文&模型涨点之——特征选择 特征选择作为机器学习与数据挖掘领域的核心预处理步骤,其重要性在当今高维数据时代日益凸显。 通过识别最具判别性的特征子集,特征选择算法能够有效缓解"维度灾难"、提升模型泛化能力&#x…...
面壁智能推出 MiniCPM 4.0 端侧大模型,引领端侧智能新变革
在 2025 智源大会期间,面壁智能重磅发布了开源模型 MiniCPM 4.0 的两个新版本(0.5B、8B),代号「前进四」。此次发布在人工智能领域引发了广泛关注,标志着端侧大模型技术取得了重大突破。 卓越性能,树立行业…...
NLP学习路线图(三十二): 模型压缩与优化
一、 核心压缩与优化技术详解 1. 知识蒸馏:智慧的传承(Knowledge Distillation, KD) 核心思想:“师授徒业”。训练一个庞大、高性能但笨重的“教师模型”(Teacher Model),让其指导训练一个轻量级的“学生模型”(Student Model)。学生模型学习模仿教师模型的输出行为(…...

javaSE复习(7)
1.KMP算法 使用KMP算法在主串 "abaabaabcabaabc" 中搜索模式串 "abaabc",到匹配成功时为止,请问在匹配过程中进行的单个字符间的比较次数是()。 10次 用于互斥时 初值为1 在一个并发编程环境中,…...
算法训练第十一天
150. 逆波兰表达式求值 代码: class Solution(object):def evalRPN(self, tokens):""":type tokens: List[str]:rtype: int"""stack []for i in tokens:if i:b int(stack.pop())a int(stack.pop())stack.append(ab)elif i-:b i…...
【联网玩具】EN 18031欧盟网络安全认证
在当今数字化时代,带联网功能的玩具越来越受到孩子们的喜爱,它们为儿童带来了前所未有的互动体验和学习机会。然而,随着这类玩具的普及,网络安全问题也日益凸显。为了保障儿童使用这类玩具时的安全与隐私,欧盟出台了 E…...
Linux 如何移动目录 (文件夹) (内含 Linux 重命名方法)
1-移动单个文件夹(类似于自动剪切和粘贴) 看看以下的例子: (base) schen744mgmt-4:~/code/sparseocc/data/nuScenes-Occupancy$ ls nuScenes-Occupancy-v0.1 nuScenes-Occupancy-v0.1.7z (base) schen744mgmt-4:~/code/sparseocc/data/nuS…...

WireShark相关技巧
文章目录 1 Wireshark如何设置解析SIP 1 Wireshark如何设置解析SIP 编辑->首选项->protocols->sip 选中sip 2 点击“编辑”->“首选项”->“protocol”->ESP ,按照如下红框显示,进行勾选,点击应用...
LLMs之Structured Output:vLLM 结构化输出指南—从约束生成到自动解析与高效实现
LLMs之Structured Output:vLLM 结构化输出指南—从约束生成到自动解析与高效实现 导读:随着大语言模型(LLM)在各类任务中的广泛应用,如何使其输出具备可控性、结构化与可解析性,成为实际部署中的关键问题。…...

DAY 45 Tensorboard使用介绍
知识点回顾: tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战:MLP和CNN模型 作业:对resnet18在cifar10上采用微调策略下,用tensorboard监控训练过程。 PS: tensorboard和torch版本存在一定的不兼容…...
LeetCode刷题 -- 542. 01矩阵 基于 DFS 更新优化的多源最短路径实现
LeetCode刷题 – 542. 01矩阵 基于 DFS 更新优化的多源最短路径实现 题目描述简述 给定一个 m x n 的二进制矩阵 mat,其中: 每个元素为 0 或 1返回一个同样大小的矩阵 ans,其中 ans[i][j] 表示 mat[i][j] 到最近 0 的最短曼哈顿距离 算法思…...
TM中,return new TransactionManagerImpl(raf, fc);为什么返回是new了一个新的实例
这是一个典型的 构造器注入 封装资源的用法 🧩 代码片段 return new TransactionManagerImpl(raf, fc);✅ 简单解释: 这行代码的意思是: 使用已经打开的 RandomAccessFile 和 FileChannel,创建并返回一个新的 TransactionManag…...
将 tensorflow keras 训练数据集转换为 Yolo 训练数据集
以 https://www.kaggle.com/datasets/vipoooool/new-plant-diseases-dataset 为例 1. 图像分类数据集文件结构 (例如用于 yolov11n-cls.pt 训练) import os import csv import random from PIL import Image from sklearn.model_selection import train_test_split import s…...
(新手友好)MySQL学习笔记(6):分组查询,正则表达式
目录 分组查询 创建分组 过滤分组 分组查询练习 正则表达式 匹配单个实例 匹配多个实例 正则表达式练习 练习答案 分组查询练习答案 正则表达式练习答案 分组查询 创建分组 group by 子句:根据一个或多个字段对结果集进行分组,在分组的字段上…...

台式机电脑CPU天梯图2025年6月份更新:CPU选购指南及推荐
组装电脑选硬件的过程中,CPU的选择无疑是最关键的,因为它是最核心的硬件,关乎着一台电脑的性能好坏。对于小白来说,CPU天梯图方便直接判断两款CPU性能高低,准确的说,是多核性能。下面给大家分享一下台式机电脑CPU天梯图2025年6月版,来看看吧。 桌面CPU性能排行榜2025 台…...
【hadoop】Flink安装部署
一、单机模式 步骤: 1、使用XFTP将Flink安装包flink-1.13.5-bin-scala_2.11.tgz发送到master机器的主目录。 2、解压安装包: tar -zxvf ~/flink-1.13.5-bin-scala_2.11.tgz 3、修改文件夹的名字,将其改为flume,或者创建软连接…...

将单体架构项目拆分成微服务时的两种工程结构
一.独立Project 1.示意图 此时我们创建一个文件夹,在这个文件夹中,创建N个Project,每一个Project对应一个微服务,组成我们的最终的项目。 2.特点 适合那种超大型项目,比如淘宝,但管理负担比较重。 二.Mave…...

Unity3D 开发中的创新技术:解锁 3D 开发的新境界
在 3D 开发的广袤天地里,Unity3D 一直是众多开发者的得力伙伴。可如今,普通的开发方式似乎难以满足日益增长的创意与效率需求。你是否好奇,凭什么别家团队能用 Unity3D 打造出令人拍案叫绝的 3D 作品,自己却总感觉差了那么一点火候…...

UOS 20 Pro为国际版WPS设置中文菜单
UOS 20 Pro为国际版WPS设置中文菜单 查看UOS操作系统系统安装国际版wps并汉化方法1:下载zh_CN.tar.gz语言包方法2:手动从国内版wps12的包中提取中文菜单解压国内版wps的包 复制中文语言包到wps国际版目录下安装Windows字体 安装开源office 查看UOS操作系统系统 # 查…...
树莓派系统中设置固定 IP
在基于 Ubuntu 的树莓派系统中,设置固定 IP 地址主要有以下几种方法: 方法一:使用 Netplan 配置(Ubuntu 18.04 及以上版本默认使用 Netplan) 查看网络接口名称 在终端输入ip link或ip a命令,查看当前所使…...

单例模式与锁(死锁)
目录 线程安全的单例模式 什么是单例模式 单例模式的特点 饿汉实现方式和懒汉实现方式 饿汉⽅式实现单例模式 懒汉⽅式实现单例模式 懒汉⽅式实现单例模式(线程安全版本) 单例式线程池 ThreadPool.hpp threadpool.cc 运行结果 线程安全和重⼊问题 常⻅锁概念 死…...
LLM基础2_语言模型如何文本编码
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 字节对编码(BPE) 上一篇博文说到 为什么GPT模型不需要[PAD]和[UNK]? GPT使用更先进的字节对编码(BPE),总能将词语拆分成已知子词 为什么需要BPE? 简…...

理解世界如淦泽,穿透黑幕需老谋
理解世界如淦泽,穿透黑幕需老谋 卡西莫多 2025年06月07日 安徽 极少主动跟别人提及恩师的名字,生怕自己比孙猴子不成器但又比它更能惹事的德行,使得老师跟着被拖累而脸上无光。不过老师没有象菩提祖师训诫孙猴子那样不能说出师傅的名字&a…...
如何确定微服务的粒度与边界
确定微服务的粒度与边界 在完成初步服务拆分之后,架构师往往会遇到另一个难题:该拆到多细?哪些功能可以归并为一个服务,哪些又必须单独部署?这就是“服务粒度与边界”的问题。本节将围绕实际架构经验,介绍…...

第三讲 Linux进程概念
1. 冯诺依曼体系结构 我们买了笔记本电脑, 里面是有很多硬件组成的, 比如硬盘, 显示器, 内存, 主板... 这些硬件不是随便放在一起就行的, 而是按照一定的结构进行组装起来的, 而具体的组装结构, 一般就是冯诺依曼体系结构 1.1. 计算机的一般工作逻辑 我们都知道, 计算机的逻…...