C/C++语言常见问题-智能指针、多态原理
文章目录
- 智能指针实现原理
- 智能指针,里面的计数器何时会改变
- `std::shared_ptr`的引用计数器:
- `std::weak_ptr`的弱引用计数器:
- `std::unique_ptr`的控制块:
- 总结:
- 智能指针和管理的对象分别在哪个区
- 面向对象的特性:多态原理
- 多态性的定义
- 多态性的类型
- 多态性的原理
- 多态性的实现步骤
- 示例代码(C++)
智能指针实现原理
在C++中,智能指针是一种自动管理内存分配和释放的工具,它们帮助程序员避免内存泄漏和其他与动态内存分配相关的问题。C++11标准引入了几种智能指针,主要包括std::unique_ptr、std::shared_ptr和std::weak_ptr。下面将分别介绍这些智能指针的实现原理。
std::unique_ptr
std::unique_ptr是一种独占所有权的智能指针,意味着同一时间只能有一个std::unique_ptr指向特定对象。它不允许复制,但可以移动。
实现原理:
- 构造函数:接受原始指针,并在构造时接管所有权。
- 析构函数:当
std::unique_ptr对象被销毁时,它会自动删除它所拥有的对象。 - 移动语义:通过移动构造函数和移动赋值运算符,可以将所有权从一个
std::unique_ptr转移到另一个,原指针变为空。 - 删除复制语义:复制构造函数和复制赋值运算符被删除,防止多个指针指向同一对象。
std::shared_ptr
std::shared_ptr是一种共享所有权的智能指针,允许多个std::shared_ptr实例共享同一对象。
实现原理:
- 引用计数:
std::shared_ptr内部维护一个引用计数器,用来跟踪有多少个std::shared_ptr实例共享同一个对象。 - 构造函数:接受原始指针,并在构造时接管所有权,同时初始化引用计数为1。
- 拷贝构造函数和拷贝赋值运算符:当
std::shared_ptr被复制时,引用计数增加。 - 移动构造函数和移动赋值运算符:移动语义允许将所有权从一个
std::shared_ptr转移到另一个,同时减少原指针的引用计数。 - 析构函数:当最后一个
std::shared_ptr被销毁或被重新赋值时,引用计数减至0,此时自动删除所管理的对象。
std::weak_ptr
std::weak_ptr是一种不控制对象生命周期的智能指针,通常与std::shared_ptr配合使用,用于解决强引用循环问题。
实现原理:
- 弱引用计数:
std::weak_ptr不增加对象的引用计数,但可以观察引用计数。 - 构造函数:可以从一个
std::shared_ptr构造,但不增加引用计数。 - 析构函数:不删除对象,只是减少内部的弱引用计数。
- 升级为
std::shared_ptr:如果对象仍然存在(即std::shared_ptr的引用计数大于0),std::weak_ptr可以被升级为一个std::shared_ptr。
总结
智能指针通过封装原始指针和自动管理内存的生命周期,减少了内存泄漏的风险。std::unique_ptr适用于单一所有权的情况,std::shared_ptr适用于需要多个指针共享同一对象的情况,而std::weak_ptr则用于打破强引用循环,提供更灵活的控制。
智能指针在C++中的底层实现通常依赖于几种基本的数据结构,这些数据结构帮助智能指针管理对象的生命周期和引用计数。以下是这些数据结构的简要说明:
std::unique_ptr
std::unique_ptr通常不需要复杂的数据结构来管理引用计数,因为它不允许共享所有权。它的实现相对简单,通常只包含一个指向管理对象的原始指针,可能还包括一个自定义删除器(如果提供了的话)。std::shared_ptr
std::shared_ptr的实现依赖于两个主要的数据结构:
- 控制块(Control Block):这是一个结构体,通常包含至少两个指针,一个指向管理的对象,另一个指向一个引用计数器。引用计数器记录了当前有多少个
std::shared_ptr实例共享这个对象。 - 引用计数器:这是一个整数值,用来跟踪有多少个
std::shared_ptr实例共享同一个对象。当最后一个std::shared_ptr被销毁或被重新赋值时,引用计数减至0,对象会被自动删除。
std::weak_ptr
std::weak_ptr的实现也依赖于控制块,但它不直接管理对象的生命周期。它包含以下元素:
- 弱引用计数器:与
std::shared_ptr的控制块中的引用计数器不同,std::weak_ptr的弱引用计数器不增加对象的引用计数,它只是用来跟踪有多少个std::weak_ptr实例观察这个对象。 - 指向控制块的指针:
std::weak_ptr包含一个指向对象控制块的指针,通过这个指针,std::weak_ptr可以访问对象的引用计数和对象本身。
实现细节
在实际的实现中,控制块可能包含更多的信息,例如:
- 自定义删除器:如果提供了自定义删除器,控制块中也会包含指向这个删除器的指针。
- 线程安全:在多线程环境中,引用计数的增加和减少需要是原子操作,以避免竞态条件。
总结
智能指针的底层实现通过控制块和引用计数器来管理对象的生命周期和共享状态。这些数据结构的设计使得智能指针能够在不牺牲性能的情况下,提供自动的内存管理和引用计数功能。
智能指针,里面的计数器何时会改变
在C++中,智能指针的引用计数器(对于std::shared_ptr)或控制块(对于std::unique_ptr和std::weak_ptr)的值会在以下情况下改变:
std::shared_ptr的引用计数器:
-
增加计数:
- 当通过拷贝构造函数或拷贝赋值运算符创建一个新的
std::shared_ptr实例时,指向同一对象的std::shared_ptr数量增加,引用计数器增加。 - 当通过
std::shared_ptr的clone函数创建一个新的std::shared_ptr时,引用计数器也会增加。
- 当通过拷贝构造函数或拷贝赋值运算符创建一个新的
-
减少计数:
- 当一个
std::shared_ptr实例被销毁(例如离开其作用域或被显式析构)时,引用计数器减少。 - 当通过移动构造函数或移动赋值运算符将
std::shared_ptr的所有权转移给另一个实例时,原实例的引用计数器减少。 - 当通过
std::shared_ptr的reset方法重置指针时,如果新的控制块与旧的不同,旧控制块的引用计数器减少。
- 当一个
std::weak_ptr的弱引用计数器:
-
增加计数:
- 当创建一个新的
std::weak_ptr实例,并且它与一个现有的std::shared_ptr共享同一个控制块时,弱引用计数器增加。
- 当创建一个新的
-
减少计数:
- 当一个
std::weak_ptr实例被销毁时,如果它之前是指向一个对象的最后一个弱引用,弱引用计数器减少。
- 当一个
std::unique_ptr的控制块:
std::unique_ptr不使用引用计数器,因为它不允许共享所有权。但是,它的控制块(如果提供了自定义删除器)会在以下情况下改变:
- 所有权转移:
- 当通过移动构造函数或移动赋值运算符将
std::unique_ptr的所有权从一个实例转移到另一个实例时,原实例的控制块变为空。
- 当通过移动构造函数或移动赋值运算符将
总结:
std::shared_ptr:引用计数器会在拷贝、赋值、销毁、移动和重置时改变。std::weak_ptr:弱引用计数器会在创建和销毁时改变,但不会直接影响对象的生命周期。std::unique_ptr:不使用引用计数器,控制块在所有权转移时改变。
智能指针的这些机制确保了对象的生命周期被正确管理,同时避免了内存泄漏和其他资源管理错误。
智能指针和管理的对象分别在哪个区
(智能指针本身在栈区,托管的资源在堆区,利用了栈对象超出生命周期后自动析构的特征,所以无需手动delete释放资源)
在C++中,智能指针和它们管理的对象的存储位置取决于它们是如何被创建和使用的。以下是智能指针和它们管理的对象可能位于的内存区域:
-
智能指针本身:
- 栈区(Stack):如果智能指针是局部变量或者作为函数参数传递,它们通常存储在栈区。当函数结束时,智能指针会被自动销毁,它们的析构函数会被调用,从而管理的对象也会被适当地释放。
- 堆区(Heap):如果智能指针是通过
new操作符或者std::make_shared等函数动态分配的,它们将存储在堆区。这种情况下,需要手动调用delete来释放智能指针本身占用的内存,但它们管理的对象会在智能指针被销毁时自动释放。
-
管理的对象:
- 堆区(Heap):智能指针管理的对象通常存储在堆区。这是因为对象的生命周期可能需要比创建它们的函数更长,或者对象的大小可能在编译时未知,因此不能在栈区分配。
- 例如,
std::shared_ptr和std::unique_ptr通常指向堆区中的对象。当智能指针被销毁时,它们会自动释放指向的对象。
-
控制块:
- 对于
std::shared_ptr来说,还有一个与之关联的控制块,它也存储在堆区。控制块包含了引用计数器,用于跟踪有多少个std::shared_ptr实例共享同一个对象。当最后一个引用被销毁时,控制块中的引用计数器会减少到0,对象和控制块都会被释放。
- 对于
-
弱引用计数器:
- 对于
std::weak_ptr来说,它不直接管理对象的生命周期,但是它可能包含一个指向控制块的指针,这个指针用于跟踪弱引用的数量。这个计数器通常也存储在控制块中,与std::shared_ptr的控制块相同。
- 对于
总结来说,智能指针本身可以位于栈区或堆区,而它们管理的对象通常位于堆区。智能指针的析构函数确保了对象在不再需要时被自动释放,这是通过控制块中的引用计数器来管理的。这种设计模式减少了手动管理内存的需要,从而降低了内存泄漏和其他资源管理错误的风险。
面向对象的特性:多态原理
面向对象编程(OOP)是一种编程范式,它使用“对象”来设计应用程序和程序结构。在面向对象编程中,多态性(Polymorphism)是一个核心特性,它允许你以统一的方式处理不同类型的对象。多态性使得程序更加灵活和可扩展。
多态性的定义
多态性指的是同一个操作作用于不同的对象时,可以有不同的解释和不同的执行结果。在编程中,这通常意味着同一个方法或函数可以被不同的对象以不同的方式实现。
多态性的类型
-
编译时多态(静态多态):
- 方法重载(Overloading):同一个类中有多个同名方法,它们的参数列表不同(参数类型、数量或顺序不同)。
- 操作符重载(Operator Overloading):对已有的操作符赋予新的功能,使其可以作用于用户定义的类型。
-
运行时多态(动态多态):
- 方法重写(Overriding):在派生类中重新定义基类的方法。这是通过使用虚函数(virtual function)实现的,允许在运行时根据对象的实际类型调用相应的方法。
多态性的原理
- 虚函数表(Virtual Table):在C++中,运行时多态性是通过虚函数表实现的。每个包含虚函数的类都有一个虚函数表,这个表包含了指向类中所有虚函数的指针。
- 虚函数指针:每个对象都有一个指向其类虚函数表的指针。当通过基类指针或引用调用虚函数时,会使用这个指针来查找并调用正确的函数。
多态性的实现步骤
- 声明虚函数:在基类中声明函数为虚函数(使用
virtual关键字)。 - 派生类重写:在派生类中重写这个虚函数。
- 使用基类指针或引用:通过基类的指针或引用来调用虚函数。
示例代码(C++)
#include <iostream>class Animal {
public:virtual void speak() {std::cout << "Animal makes a sound\n";}virtual ~Animal() {} // 虚析构函数以确保派生类对象的正确析构
};class Dog : public Animal {
public:void speak() override {std::cout << "Dog barks\n";}
};class Cat : public Animal {
public:void speak() override {std::cout << "Cat meows\n";}
};int main() {Animal* myAnimal = new Dog();myAnimal->speak(); // 输出 "Dog barks"Animal* anotherAnimal = new Cat();anotherAnimal->speak(); // 输出 "Cat meows"delete myAnimal;delete anotherAnimal;return 0;
}
在这个例子中,Animal类有一个虚函数speak(),Dog和Cat类分别重写了这个函数。通过基类指针调用speak()时,会根据对象的实际类型调用相应的函数,这就是多态性的体现。
多态性是面向对象编程中非常重要的特性,它提高了代码的可重用性和可维护性,同时也使得代码更加灵活和易于扩展。
C++11 是 C++ 语言的一次重大更新,它引入了许多新特性,使得 C++ 编程更加现代化和高效。以下是你提到的几个关键特性的详细介绍:
-
智能指针(Smart Pointers):
std::unique_ptr:表示独占所有权的智能指针,确保同一时间只有一个智能指针可以指向某个对象。当std::unique_ptr被销毁时,它所拥有的对象也会被自动删除。std::shared_ptr:表示共享所有权的智能指针,多个std::shared_ptr可以指向同一个对象,对象会在最后一个指向它的智能指针被销毁时自动删除。std::weak_ptr:用于解决std::shared_ptr相互引用时可能出现的循环引用问题。std::weak_ptr不会增加对象的引用计数,它可以用来观察一个对象而不增加其引用计数。
-
类型推导(Type Deduction):
auto关键字:允许编译器自动推导变量的类型。例如,auto x = someFunction();中x的类型将与someFunction()的返回类型相同。decltype:用于推导表达式的类型,但不创建一个值。例如,decltype(someVariable)将推导出someVariable的类型。
-
Lambda 表达式:
- Lambda 表达式是一种匿名函数,可以在需要的地方定义并立即使用。它们通常用于简短的函数对象,或者作为参数传递给函数。
- 基本语法:
[capture](parameters) -> return_type { function_body }。 capture可以是值捕获(如x)或引用捕获(如&x),也可以是this指针的捕获。parameters是 Lambda 表达式的参数列表,如果省略参数列表,则默认为()。return_type是 Lambda 表达式的返回类型,如果省略,则由编译器根据function_body推导。
-
多种容器的使用:
std::array:固定大小的数组,大小在编译时确定。std::vector:动态大小的数组,可以自动调整大小。std::deque:双端队列,支持快速的随机访问和在两端快速插入/删除。std::list:双向链表,支持快速的插入和删除操作。std::forward_list:单向链表,只支持在头部和尾部快速插入/删除。std::map和std::unordered_map:键值对的集合,分别基于红黑树和哈希表实现。std::set和std::unordered_set:元素集合,分别基于红黑树和哈希表实现。
-
多线程的使用:
std::thread:表示一个线程,可以用来创建和管理线程。std::mutex:互斥锁,用于保护共享数据,防止多个线程同时访问。std::lock_guard和std::unique_lock:RAII(资源获取即初始化)风格的锁,用于自动管理互斥锁的锁定和解锁。std::condition_variable:条件变量,用于线程间的同步,允许一个或多个线程等待某个条件成立。std::future和std::promise:用于线程间的数据传递,std::promise可以设置一个值,而std::future可以获取这个值。
这些特性极大地增强了 C++ 的表达力和灵活性,使得编写现代、高效、安全的代码变得更加容易。
相关文章:
C/C++语言常见问题-智能指针、多态原理
文章目录 智能指针实现原理智能指针,里面的计数器何时会改变std::shared_ptr的引用计数器:std::weak_ptr的弱引用计数器:std::unique_ptr的控制块:总结: 智能指针和管理的对象分别在哪个区面向对象的特性:多…...
Python 实现日志备份守护进程
实训背景 假设你是一名运维工程师,需要为公司的监控系统开发一个简单的日志备份守护进程。该进程需满足以下需求: 后台运行:脱离终端,长期监控指定目录(如 /var/log/app/)中的日志文件。自动备份…...
Electricity Market Optimization 探索系列(VII)- 直流潮流方程的推导及例题
本文参考书籍:电力经济与电力市场,甘德强,杨莉,冯冬涵 著 link \hspace{1.6em} 文章的结构如下:围绕电力传输系统中短线路的等值等效模型,从节点注入功率的角度和线路功率的角度分析电网中的潮流࿰…...
DataOutputStream 终极解析与记忆指南
DataOutputStream 终极解析与记忆指南 一、核心本质 DataOutputStream 是 Java 提供的数据字节输出流,继承自 FilterOutputStream,用于写入基本数据类型和字符串的二进制数据。 作用:1.将java程序中的数据直接写入到文件,写到文…...
Spring AI与通义千问的完美结合:构建智能对话应用
Spring AI是Spring生态系统中的新成员,它为开发人员提供了一套简单而强大的工具,用于集成各种AI大模型。本文将介绍如何使用Spring AI与阿里云通义千问大模型进行集成,构建智能对话应用,帮助你快速掌握AI应用开发的核心技能。 引言 随着人工智能技术的快速发展,越来越多的…...
路由过滤实验
实验拓扑以及要求 此实验总结 1.ip-prefix 拒绝192.168.4.1 32,这样写的话并不会匹配192.168.4.1 32,需要加上范围less-eq 32,也就是说,192.168.4.1 32只是规则的范围,匹配还是得写范围 2.router-policy适合用在边界路由器引入 filter-policy都可以用 配置IP 配置ospf,rip …...
Idea连接远程云服务器上的MySQL,开放云服务器端口
1.开放云服务器的3306端口 (1)进入到云服务器的控制台 (2)点击使用的云服务器 (3)点击 配置安全组规则 (4)添加规则 (5)开放端口 2.创建可以远程访问…...
Oracle查询大表的全部数据
2000w的大表 表结构如下,其中id是索引 查询处理慢的写法 List<String> queryLoidForPage(Integer startNum,Integer endNum){try {Connection oracleConnection initBean.oracleConnection;Statement stmt oracleConnection.createStatement();// 4.执行查…...
Java锁的分类与解析
在多线程编程中,锁是确保共享资源不会同时被多个线程访问的关键工具。Java 提供了多种锁的实现方式,可以根据不同的需求选择适合的锁。本文将从多个维度对 Java 的锁进行分类,并对每种锁的特性进行详细解析。 锁的分类 锁的分类并没有唯一标…...
PyTorch生成式人工智能实战(1)——神经网络与模型训练过程详解
PyTorch生成式人工智能实战(1)——神经网络与模型训练过程详解 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 前向传播3.1 计算隐藏层值3.2 执行非线性激活3.3 计算输出层值3.4 计算损失值3.5 实现前…...
基于X86/Nvidia+FPGA大模型具身智能机器人控制器解决方案,同时拥有算力与实时的便利
2025年成为人形机器人产业化元年,行业已突破早期实验室研发阶段,进入"场景验证量产爬坡"新周期,预计2031年具身智能市场规模有望突破万亿元。这一进程的背后,是硬件算力、实时控制、环境适应等底层技术的系统性突破——…...
使用 OpenRewrite 简化 Java 和 SpringBoot 迁移
大家好,这里是架构资源栈!点击上方关注,添加“星标”,一起学习大厂前沿架构! 移民的挑战 随着 Spring Boot 2.x 等旧版本即将到期且不再获得支持,迁移到较新版本对于安全性、兼容性和性能改进至关重要。但…...
2025中国移动云智算大会回顾:云智变革,AI+跃迁
4月10日,2025中国移动云智算大会在苏州举办。会上,中国移动开启“由云向智”新范式,以“智”为核心开辟算网新生态,彰显其在AI新时代的战略远见与技术引领力。 “云智算”将如何通过算网基础设施与人工智能核心技术的深度融合&am…...
ubuntu 2204 安装 vcs 2023
系统 : Ubuntu 22.04.1 LTS vcs 软件 : 有已经安装好的软件(位于redhat8.10),没找到安装包 . 安装好的目录文件 占用 94 G注意 : 该虚拟机(包括安装好的软件)有114G,其中安装好的目录文件占用94GB // 即 我要把 这里 已经安装好的软件(包括scl/vcs/verdi 和其他软件) 在 …...
C#实例化类型详解:从概念到实战
在C#编程中,实例化类型是构建程序的核心操作。本文将通过通俗易懂的案例,深入解析类型实例化的原理与实践技巧,帮助开发者快速掌握这一基础但至关重要的概念。 实例化类型是什么? 定义 通过类型模板创建具体对象的过程称为实例…...
Redis的Key的过期策略
我们都知道Redis的键值对是可以设置过期时间的,那么就会涉及到一个问题,Redis到底是如何做到响应快的同时有能快速地释放掉过期的键值对的呢?不卖关子了,直接说答案,那就是Redis两个策略:定期删除和惰性删除…...
python爬虫降低IP封禁,python爬虫除了使用代理IP和降低请求频率,还有哪些方法可以应对IP封禁?
文章目录 前言1. 利用 CDN 节点2. 模拟真实用户行为3. 使用 IP 池轮换策略4. 处理 Cookie 和会话信息5. 分布式爬虫 前言 除了使用代理 IP 和降低请求频率,以下这些方法也能应对 IP 封禁: Python 3.13.2安装教程(附安装包)Python…...
Unity URP Moblie AR示例工程,真机打包出来,没阴影
效果: unity ar示例演示 现象: 真机打包测试私活没有阴影 Unity版本:2022.3.4f1c1 分析原因: Prefab :ARFeatheredPlane中也有材质,一个用于环境遮挡,一个用于阴影接受。 按理说有啊。 urp …...
单片机 | 基于51单片机的自动循迹小车设计
以下是一个基于51单片机的自动循迹小车设计详解,包含原理、公式和完整代码: 一、系统原理 核心模块: 传感器:红外对管(TCRT5000)x4主控芯片:STC89C52RC(51单片机)电机驱动:L298N驱动模块电源:7.4V锂电池(电机) + 5V稳压(单片机)工作原理: 红外对管发射红外线,…...
【AI】——结合Ollama、Open WebUI和Docker本地部署可视化AI大语言模型
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
Hook插件
hook插件 1.概念 在JavaScript中,hook是一种能够拦截和修改函数或方法行为的技术。通过使用hook,开发者可以在现有的函数执行前、执行后或者替换函数的实现逻辑。hook目的是找到函数入口以及一些参数变化,便于分析js逻辑。 2.hook的作用&a…...
双 Token 与 单 Token 优缺点
双Token与单Token认证机制对比 在Web应用开发中,身份认证和授权是保障系统安全的核心环节。随着技术演进,基于Token的认证机制逐渐取代传统Session方案,而双Token与单Token架构的选型争议也日益成为开发者关注的焦点。本文将从技术原理、优缺…...
Android 不插SIM卡,手机不能拨打紧急电话;2g+gsm配置才支持112紧急拨号
[DESCRIPTION] 不插SIM卡,手机不能拨打紧急电话 Root Cause 手机没有写入合法的IMEI;或者当地的某个运营商不支持紧急电话,而手机正好选上了这个运营商;或者当地的某个运营商不支持无SIM卡的紧急电话,而手机正好选上了这个运营商 [SOLUTION] …...
java 多线程之Worker Thread模式(Thread Pool模式)
Worker Thread模式 Worker的意思是工作的人,在Worker Thread模式中,工人线程Worker thread会逐个取回工作并进行处理,当所有工作全部完成后,工人线程会等待新的工作到来。 Worker Thread模式也被成为Background Threadÿ…...
在 MoonBit 中引入 Elm 架构:用简单原则打造健壮的 Web 应用
Elm 是一种纯函数式编程语言,专为构建前端 Web 应用程序而设计。它编译为 JavaScript,强调简洁性、性能和健壮性。 纯函数式的含义是函数没有副作用,这使得代码更易于理解和调试。通过强大的静态类型检查,Elm 确保应用程序不会抛…...
CMD命令行笔记
CMD命令行笔记,涵盖常用命令及实用技巧,适合快速查阅: 一、基础操作 打开CMD Win R → 输入 cmd → 回车管理员模式:右键开始菜单 → 选择“命令提示符(管理员)” 常用命令 help:查看所有命令…...
Python自动化办公
第五篇:Python自动化办公:10行代码搞定重复性工作 适合读者:职场人士、数据分析师 | 阅读时长:12分钟 引言 每天重复处理Excel、PDF或邮件?Python可以帮你自动化这些枯燥任务,节省90%的时间。本文通过实际…...
PDF 转换为 Word、HTML、LaTeX 和 Markdown 格式
PDF 转换为 Word、HTML、LaTeX 和 Markdown 格式 1. Doc2XReferences https://doc2x.com/ 1. Doc2X References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/ [2] GPT 学术优化 (GPT Academic), https://github.com/binary-husky/gpt_academic [3] 学术版 GPT 网页…...
C#中async await异步关键字用法和异步的底层原理
目录 C#异步编程一、异步编程基础二、异步方法的工作原理三、代码示例四、编译后的底层实现五、总结 C#异步编程 一、异步编程基础 异步编程是啥玩意儿 就是让程序在干等着某些耗时操作(比如等网络响应、读写文件啥的)的时候,能把线程腾出来…...
shardingsphere-jdbc集成Seata分布式事务
1、导入相关依赖 <!-- shardingsphere-jdbc --><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc</artifactId><version>5.5.1</version></dependency><!-- shardingspher…...
