[C++随想录] 继承
继承
- 继承的引言
- 基类和子类的赋值转换
- 继承中的作用域
- 派生类中的默认成员函数
- 继承与友元
- 继承与静态成员
- 多继承的结构
- 棱形继承的结构
- 棱形虚拟继承的结构
- 继承与组合
继承的引言
- 概念
继承(inheritance)机制是面向对象程序设计使代码可以复用
的最重要的手段,它允许程序员在保
持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继 承是类设计层次的复用。
- 定义
class People
{
public:People(string name = "John", int age = 18){_name = name;_age = age;}void print() {cout << "姓名->" << _name << endl;cout << "年龄->" << _age << endl;}protected:int _age ;string _name;
};class Student : public People
{
public:Student(string name = "muyu", int age = 20, string id = "9210401227"):People(name, age){_id = id;}protected:string _id;
};class Teacher : public People
{
public:Teacher(){People::_name = "mutong";People::_age = 22;}protected:int _JobNumber;
};int main()
{People p;p.print();cout << endl;Student s;s.print();cout << endl;Teacher t;t.print();return 0;
}
运行结果:
姓名->John
年龄->18姓名->muyu
年龄->20姓名->mutong
年龄->22
-
解释
class Student : public People
People类是父类/ 基类
, Student类是子类/ 派生类
Student类继承People类的本质就是复用
⇒Student 对象可以使用 People类里面的成员
成员包括成员变量 和 成员函数
.
成员变量是在对象空间内的, 而成员函数是不在对象空间内的, 属于整个类.
成员的访问限定符
有三种public, protected, private
继承方式不同 && 基类成员的访问限定符不同
⇒决定了基类的成员在派生类中的存在情况
-
继承的方式
继承方式有三种:public, protected, private
成员的限定符
有三种public, protected, private
所以, 一共有九种继承方式
👇👇👇
- 基类中的private成员, 在派生类中都是
不可见的
- 不可见 和 private成员是不一样的, private成员是
类里面可以访问, 类外面不可访问
, 不可见是类里面看不见/ 不可访问, 类外面不可访问
- 不可见 和 private成员是不一样的, private成员是
- 其余继承方式, 派生类中的情况是
继承方式 和 类成员访问限定符中 权限小的那一个
- 权限的大小:
public > protected > private
- 权限的大小:
- 父类如果是
class, 默认继承是 私有继承
, 父类如果是struct, 默认继承是 公有继承
. 不过建议显示继承方式 - 常用的继承方式为
图中绿色的区域
⇐ 继承的本质是复用
, 私有继承 和 基类中的私有成员在继承中是没有复用的意义的.
- 基类中的private成员, 在派生类中都是
-
为什么 派生类没有
print函数
, 但能调用 print函数?
我们可以认为子类对象里面包含两个部分: 父类对象成员变量 + 子类本身成员变量
子类对象中的成员变量 = 自己本身的成员变量 + 父类的成员变量 (受访问限定符 和 继承方式共同限制)
子类对象中的成员函数 = 自己本身的成员函数 + 父类的成员函数 (受访问限定符 和 继承方式共同限制)
print函数
是公有继承 && 访问限定符是公有 ⇒ 子类对象可以调用 -
为什么在
Teacher类中
可以People::_name = "mutong";
我们已经知道了 派生类对象的基本结构了.
那么派生类对象在初始化阶段, 即调用默认构造
是先父类还是先子类呢?
通过调试, 我们发现:子类对象调用构造函数的时候, 先调用父类的默认构造函数去初始化子类中父类对象的那一部分, 然后在调用子类对象的默认构造函数
Person类中有默认构造函数, 但是我们想改变一下Teacher类对象中的 关于父类对象的那一部分
, 那我们该怎么做呢?
首先, 我们不能直接写
_name = "mutong";
_age = 22;
因为受 域
的影响, 域是编译器在编译阶段查找变量的规则.
虽然, 我们可以认为子类对象中有 父类对象成员 + 子类对象成员
, 但彼此是 独立的
.
调用默认构造函数还是去 Person类中去调用
编译器在 编译阶段
默认查找的顺序是 局部域 , 子类域, 父类域, 全局域
我们在子类中去给父类对象成员赋值 ⇒ 我们应该告诉编译器, 这个变量直接去父类中去查找就OK
即, 这个时候我们要用 Person(父类)::
- 为什么在
Student类中
可以:People(name, age)
子类对象调用构造函数的时候, 先调用父类的默认构造函数去初始化子类中父类对象的那一部分, 然后在调用子类对象的默认构造函数.
那么如果父类对象没有默认构造函数呢?
我们就需要在子类的初始化列表处 显示调用父类的构造
基类和子类的赋值转换
int main()
{People p;Student st;st = p; // errorp = st; // 可以进行转换return 0;
}
- 父类对象
不能
赋值给子类对象, 而子类对象可以
赋值给父类对象
可以这样想:子类对象的成员 > 父类对象的成员
⇒可以 变小一点, 但不能变大一点
父类对象 = 子类对象, 属于不同类型之间的赋值
⇒ 一般都会发生 类型转换
⇒ 类型转换, 那就意味着要产生 临时常量拷贝
. 但结果真的如我们想的这般吗?
- 验证
父类对象 = 子类对象
是否有临时常量拷贝
拷贝是常量的
⇒ 要进行区分, 我们可以使用引用 &
如果生成了临时拷贝, 我们用普通引用 就会导致权限的放大
, 就会报错
如果没有生成临时拷贝, 我们用普通引用, 就是权限的平移
, 就不会报错
int main()
{// 类型转换int i = 0;double d = i;// double& dd = i // errorconst double& dd = i;// 赋值兼容转换Student st;People ps = st;People& p = st;return 0;
}
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫 切片 或者切割
. 寓意把派生类中父类那部分切来赋值过去
🗨️那么这个切片是怎样完成的呢?
继承中的作用域
🗨️在继承过程中, 可能会出现 父类中的成员名 和 子类中的成员名相同的情况
, 那么派生类对象调用该成员会是怎样的情况呢?
- 先看下面的代码:
class People
{
public:People(string name = "John", int age = 18){_name = name;_age = age;}void print() {cout << "class People" << endl;}protected:int _age ;string _name;
};class Student : public People
{
public:Student(string name = "muyu", int age = 20, string id = "9210401227"):People(name, age){_id = id;}void print(){cout << "class Student : public People" << endl;}protected:string _id;
};void test1()
{Student st;st.print();
}int main()
{test1();return 0;
}
运行结果:
class Student : public People
父类和子类中都有 print函数
, 通过结果显示 派生类内部的print函数
这是因为 域
, 跟上面的People::_name = "muyu";
是一样的道理
那么, 如果我们非要通过派生类对象 调用基类中的print函数
呢?👇👇👇
void test1()
{Student st;st.People::print();
}
总结:
- 子类和父类中的成员尽量不同名!
- 上面的例子, 子类和父类有同名的成员, 子类隐藏父类的成员, 这种关系叫做
隐藏/ 重定义
- 注意:
隐藏 != 重载
重载的前提条件是同一作用域
, 而隐藏是父类和子类成员同名
隐藏 != 重写
隐藏是子类中同名成员隐藏父类中同名成员
, 而重写是子类中重写父类有关函数的实现
- 注意:
派生类中的默认成员函数
6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类
中,这几个成员函数是如何生成的呢?
- 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。
如果基类没有默认 的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
- 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
- 派生类的operator=必须要调用基类的operator=完成基类的复制。
- 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。 - 派生类对象初始化先调用基类构造再调派生类构造。
- 派生类对象析构清理先调用派生类析构再调基类的析构。
- 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲
解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加
virtual的情况下,子类析构函数和父类析构函数构成隐藏关系
class Person
{
public:Person(string name = "muyu", int age = 20):_name(name),_age(age){cout << "Person()" << endl;}Person(const Person& tem){_name = tem._name;_age = tem._age;cout << "Person(const Person& tem)" << endl;}Person& operator=(const Person& tem){_name = tem._name;_age = tem._age;return *this;cout << "Person& operator=(Person& tem)" << endl;}~Person(){cout << "~Person()" << endl;}protected:string _name;int _age;
};class Student : public Person
{
public:Student(const string name,const int age, const int num): Person(name,age), _num(num){cout << "Student()" << endl;}Student(const Student& s): Person(s), _num(s._num){cout << "Student(const Student& s)" << endl;}Student& operator = (const Student& s){cout << "Student& operator= (const Student& s)" << endl;if (this != &s){Person::operator =(s);_num = s._num;}return *this;}~Student(){cout << "~Student()" << endl;}
protected:int _num; //学号
};void test2()
{Student st1("牧童", 20, 20230101);Student st2(st1);Student st3("沐雨", 18, 20230102);st3 = st1;}int main()
{// test1();test2();return 0;
}
运行结果:
Person()
Student()
Person(const Person& tem)
Student(const Student& s)
Person()
Student()
Student& operator= (const Student& s)
~Student()
~Person()
~Student()
~Person()
~Student()
~Person()
🗨️其他函数都是 先父类, 后子类
, 唯独 析构函数 先子类后父类
?
- 首先, 构造函数是
先父类, 后子类
栈
, 先进后出 ⇒ 析构的时候,先子类, 后父类
.
其次, 父类可以调用子类的成员, 而子类不能调用父类的成员
如果先析构父类, 如果子类对象还想调用父类的成员,那就完蛋了!
🗨️在子类的析构函数中, 调用父类的析构函数
- 首先,
~Student(){~Person(); // 提示有一个重载cout << "~Student()" << endl;}
纳闷? 这个还能有重载?
因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个我们后面会讲
解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor().
那么子类和父类中的 析构函数名 都是 destruction
⇒ 那么就构成了隐藏关系
那么我们在子类中调用父类的析构函数应该如下:
~Student(){Person::~Person();cout << "~Student()" << endl;}
结果如下:
- 编译器默认帮我们
先调用了父类的析构函数
⇒不信任我们用户, 由编译器自己完成
继承与友元
基类的友元, 派生类不会继承, 即基类的友元不能访问 子类中的 私有和保护成员
// 类的声明
class Student;class Person
{
public:friend void Display(const Person& p, const Student& s);protected:string _name; // 姓名};class Student : public Person
{
public:// friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 学号};void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl; // error: “Student::_stuNum”: 无法访问 protected 成员(在“Student”类中声明)
}void test3()
{Person p;Student s;Display(p, s);
}int main()
{test3();return 0;
}
解决方法就是: 让 Display函数也充当 子类的友元
👇👇👇
class Student : public Person
{
public:friend void Display(const Person& p, const Student& s);
protected:int _stuNum; // 学号};
继承与静态成员
基类中定义了一个静态成员, 则在整个继承体系中, 仅此一份. 子类不会单独拷贝一份, 继承的是使用权
🗨️只创建子类对象, 问一共创建了多少个子类对象?
- 1. 可以在子类的默认构造中创建一个静态成员变量.
class A
{
public:A(){}
};class B :public A
{
public:B(){_count++;}
public:static int _count;
};int B::_count = 0;void test4()
{B b1;B b2;B b3;B b4;B b5;B b6;cout << "子类中的个数->" << B::_count << endl;}int main()
{test4();return 0;
}
运行结果:
子类中的个数->6
- 可以在父类的默认构造中创建一个静态成员变量.
class A
{
public:A(){++_count;}
public:static int _count;
};int A::_count = 0;class B :public A
{};void test4()
{B b1;B b2;B b3;B b4;B b5;B b6;cout << "子类的个数->" << A::_count << endl;}int main()
{test4();return 0;
}
运行结果:
子类中的个数->6
多继承的结构
一个子类只有一个直接父类, 叫做 单继承
一个子类有两个及以上的父类, 叫做 多继承
-
单继承的结构
其实在内存中不是这样 "点开" 的关系, 而是连续的空间
-
多继承的机构
当然, 也是连续的空间
棱形继承的结构
多继承会有一种情况是 棱形继承
D继承B和C, B和C又同时继承A ⇒ 就会导致D对象中有两个A对象成员
这样就会导致 冗余性和二义性
其实, 解决 访问不明确/ 二义性
可以使用 基类::
但是 内存中D还是存储了两份 A类对象 造成的数据冗余性问题还没解决呢?
棱形虚拟继承的结构
棱形虚拟继承解决的就是 数据冗余性 和 二义性的问题
通过 内存窗口
, 我们发现:
- 把A从B 和 C中抽出来了, 让A既不属于B, 也不属于C
- B和C类中多了一个位置出来
- B和C类中多了一个位置的用处是什么?
我们发现: 地址指向的空间第一个位置是 0, 第二个位置分别是20(十六进制转二进制) 和 12(十六进制转二进制)
虽然, 把A类单独放在一个空间, 但A类中的成员还是B和C类得一部分
=>
这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针
,这两个表叫虚基表
。虚基表中存的偏移量
。通过偏移量可以找到下面的A。
那么这个时候, 我们修改A类的对象, 就不会有 冗余性和二义性的问题了
👇👇👇
继承与组合
继承是一种 is-a
的关系, 是一种 白箱复用
, 子类跟父类之间的 耦合度高
对象
组合是一种 has-a
的关系, 是一种 黑箱复用
, 耦合度低
-
is-a 和 has-a
is-a,就表示子类是一个特殊的父类
has-a, 就表示A对象中有B对象
-
白箱复用 和 黑箱复用
白箱复用,透明的
, 即子类知道父类内部的细节, 方法的实现
黑箱复用,不透明的
, 即对象之间不知道彼此的内部的细节
-
耦合度
打个比方:
父类A中的成员 有20个是public, 80个是protected的; 派生类是public继承
那么在派生类B中, A的成员都是可见的 ⇒耦合度是 100%
同样的,
A对象中的成员, 有20个是public, 80个是protected的;
那么B对象想用A对象里面的成员, 只能使用 20个public的成员 ⇒耦合度是 20%
🗨️那对象组合这么好, 我们就用对象组合, 不用继承了是吧?
- 首先,
存在即合理 ⇒ 全部都这样, 或者全部都那样的想法就是错误的
- 合理使用: 符合
is-a
关系的就使用继承
; 符合has-a
关系就使用对象组合
; 如果既符合has-a, 又符合 is-a
关系使用对象组合
- 实现
多态
, 必须使用继承
- 合理使用: 符合
学后反思:
- 什么是菱形继承?菱形继承的问题是什么?
- 什么是菱形虚拟继承?如何解决数据冗余和二义性的
- 继承和组合的区别?什么时候用继承?什么时候用组合?
与朋友论学,须委曲谦下,宽以居之。 — — 王阳明
译文:与朋友谈论学问,必须婉转曲从谦虚下问,与之宽和相处
相关文章:

[C++随想录] 继承
继承 继承的引言基类和子类的赋值转换继承中的作用域派生类中的默认成员函数继承与友元继承与静态成员多继承的结构棱形继承的结构棱形虚拟继承的结构继承与组合 继承的引言 概念 继承(inheritance)机制是面向对象程序设计使代码可以 复用的最重要的手段,它允许程序…...
ARM-day9
按键控制小灯、蜂鸣器、风扇,按一次启动,第二次关闭 key_it.c #include "key_it.h"//按键3的配置 void key3_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);GPIOF->MODER & (~(0x3<<16));EXTI->E…...
2386: [余姚2015] 幸运数字(luck)
目录 题目描述 输入 输出 样例输入 样例输出 提示 来源: 代码: 题目描述 今年圣诞节,小明收到了很多礼物,每个礼物上都有一个数字,表示对小明的祝福。可是小明有自己的想法,对小明来说,4或者7的倍数…...
【JUC系列-13】深入理解DelayQueue延迟队列的底层原理
JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...

Leetcode---365周赛
题目列表 2873. 有序三元组中的最大值 I 2874. 有序三元组中的最大值 II 2875. 无限数组的最短子数组 2876. 有向图访问计数 一、有序三元组中的最大值I 看一眼该题的数据范围,直接三层for循环暴力枚举,时间复杂度O(n^3),代码如下 class…...

Java使用opencv实现人脸识别、人脸比对
1. opencv概述 OpenCV是一个开源的计算机视觉库,它提供了一系列丰富的图像处理和计算机视觉算法,包括图像读取、显示、滤波、特征检测、目标跟踪等功能。 opencv官网:https://opencv.org/ opencv官网文档:https://docs.opencv.or…...

Redis HyperLogLog的使用
Redis HyperLogLog知识总结 一、简介二、使用 一、简介 Redis HyperLogLog是一种数据结构,用于高效地计算基数(集合中唯一元素的数量)。它的主要作用是用于在内存中高效地存储和计算大量数据的基数,而无需完全存储所有的数据。Hy…...
Apisix-Ingress服务发现详解
apisix Apache APISIX 是一个基于微服务 API 网关,其不仅可以处理南北向的流量,也可以处理东西向的流量即服务之间的流量。Apache APISIX 集成了控制面板和数据面,与其他 API 网关相比,Apache APISIX 的上游、路由、插件全是动态的…...

spring6-事务
文章目录 1、JdbcTemplate1.1、简介1.2、准备工作1.3、实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返回对象④查询数据返回list集合⑤查询返回单个的值 2、声明式事务概念2.1、事务基本概念①什么是事务②事务的特性 2.2、编程式事务2.3、声明式事务 3、基于注解的…...

JavaFx学习问题2--音频、视频播放失败情况
文章目录 一、路径注意事项:① 用相对路径的时候别忘了前面的斜杠② uri问题 二、播放不了的问题① 获取的媒体文件路径本身就是不对的② 必须是uri③ 特殊情况 额外收获: 一、路径注意事项: 完整代码如下: import javafx.application.Application; im…...
第55节—— redux-toolkit中的createReducer——了解
一、概念 当我们使用 Redux 开发应用程序时,一个非常重要的概念就是 reducer。一个 reducer 是一个纯函数,它接受先前的状态和一个动作,然后返回一个新状态。每个动作都会引起状态的变化,从而使应用程序状态管理更加清晰和可控。…...

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)
基于bilibili狂神说JUC并发编程视频所做笔记 概述 什么是JUC JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务:普通的线程代码中,我们常使用Runnable接口 但Runnable没有返…...
深入了解 Java 中的时间信息定义、转换、比较和操作
1. 简介 在过去的传统Java日期处理中,经常面临着一些问题。比如,java.util.Date和java.util.Calendar在表示日期和时间时存在着一些奇怪的行为,如月份从0开始计数、对日期进行格式化的方式繁琐不直观等。这些问题给开发带来了一定的困扰。 …...

2023年中国智能矿山发展历程及趋势分析:智能矿山健康有序发展[图]
智能矿山系统对矿山生产提质增效的效果已经开始显现:对不合规、有风险的行动进行及时预警,减少安全事故发生概率,避免因停产整顿产生的巨额亏损;精细化管理整个生产流程,避免过往传统粗放的流程导致的浪费,…...
acwing算法基础之基础算法--整数离散化算法
目录 1 知识点2 模板 1 知识点 整个范围很大,但存在的数据点很少。比如从 − 1 0 9 -10^9 −109到 1 0 9 10^9 109,但总共只有 1 0 6 10^6 106个数。 可以采用离散化的思想来做,即将离散的大数值映射成连续的小数值(一般是 1 , …...

基于SSM框架的安全教育平台
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...

Kafka生产者使用案例
1.生产者发送消息的过程 首先介绍一下 Kafka 生产者发送消息的过程: 1)Kafka 会将发送消息包装为 ProducerRecord 对象, ProducerRecord 对象包含了目标主题和要发送的内容,同时还可以指定键和分区。在发送 ProducerRecord 对象前,…...

EasyX图形库实现贪吃蛇游戏
⭐大家好,我是Dark Falme Masker,学习了动画制作及键盘交互之后,我们就可以开动利用图形库写一个简单的贪吃蛇小游戏,增加学习乐趣。 ⭐专栏:EasyX部分小游戏实现详细讲解 最终效果如下 首先包含头文件 #include<stdio.h> #…...

利用 Amazon CodeWhisperer 激发孩子的编程兴趣
我是一个程序员,也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我,“为什么我们上课用的开发界面,和爸爸你…...

2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]
稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力,以及减少贵金属活性组分用量等作用,广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源:…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...