【C++】面向对象---多态(万字详解)

news2024/9/22 19:20:13

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️小林爱敲代码
      🛰️文章专栏:✈️小林的C++之路
      🛰️欢迎关注:👍点赞🙌收藏✍️留言
在这里插入图片描述

      今天给大家讲解多态,多态是面向对象的一个重要内容。也非常的抽象,所以今天尽我所能为大家分享自己对C++中多态的一些理解。



         每日一句: 世界上只有想不通的人,没有走不通的路。

大纲:
插入图片

目录

  • 💖1. 多态的概念
  • 💖2. 多态的定义及实现
    • 🌺2.2 多态的构成条件
    • 🌺2.3 虚函数
    • 🌺2.4 虚函数的重写
    • 🌺2.5 协变
    • 🌺2.6析构函数的重写
    • 🌺2.7 重载,重写(覆盖),重定义(隐藏)之间的区别
  • 💖3. override 和 final(c++11)
  • 💖4.抽象类
  • 💖 5.多态的原理
  • 💖 6.单继承和多继承的虚函数表
    • 🌺 6.2 打印虚函数表
    • 🌺 6.3 单继承的虚函数表
    • 🌺 6.4 多继承的虚函数表
  • 💖 7.多态面试问答题
  • 总结🥳:

💖1. 多态的概念

    多态的意思就是多种形态,简而言之就是 : 不同的事物做同一种行为,产生了不同的结果。
打个比方,学生和普通人买票,学生优惠7折,而普通人没有优惠。这类现象就符合多态,不同的事物(普通人,学生)做同一种行为(买票)产生了不同的结果(学生七折,普通人全款)。
blog.csdnimg.cn/c6c1467796d347858c71b7ade474d3ec.png)
而普通人和学生之间还有另外一种关系,那就是继承关系。因为学生也是人,所以构成多态的前提是不同的事物之间构成继承关系。


💖2. 多态的定义及实现

🌺2.2 多态的构成条件

    想要知道多态如何定义,那么我们必须知道多态的定义条件。构成多态的两个条件:

1.必须通过基类的指针或引用调用虚函数。
2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。


🌺2.3 虚函数

虚函数:被virtual 修饰的函数即为虚函数。

class Person
{
public:
	virtual void BuyTicket()//被virtual修饰,是虚函数
	{
		
	}
};

🌺2.4 虚函数的重写

虚函数的重写: 派生类必须有一个和基类一样(三同)的虚函数,才能构成重写。构成重写的条件:
1.派生类被重写的函数也得是虚函数(虽然不是也可以,因为会直接继承父类的虚函数)。
2.派生类被重写的函数和基类的虚函数一样 (函数名,返回值,参数都相同,协变除外)。

下面是一个虚函数构成重写的案例:

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成年人买票" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生买票" << endl;
	}
};

以上代码构成以下关系:
基类 : Person
派生类 : Student
基类的BuyTicket函数被virtual修饰,所以是虚函数。
派生类有一个和基类虚函数一模一样的虚函数。
所以派生类BuyTicket构成重写(覆盖)。

🌺2.5 协变

上面说过,被重写的函数必须与其基类对应的虚函数保持三同(返回值,函数名,参数),而协变是个例外,协变支持返回值是父子类的指针或引用。

代码案例如下:

class A{};
class B:public A
{};

class Person
{
public:
	virtual A* BuyTicket()
	{
		new A();
	}
};
class Student : public Person
{
public:
	virtual B* BuyTicket()
	{
		new B();
	}
};

返回值是父子类的指针或引用(也就是协变),一样会重写。

🌺2.6析构函数的重写

当我们在通过父类指针接收一个子类对象时,并期望释放掉这个对象。那么我们必须要让子类重写析构函数。也就是让析构函数变成虚函数,析构函数变成虚函数之后。子类会自动重写析构,这是因为在编译时析构函数的函数名会被统一处理为destructor。所以析构函数的函数名看起来不同,但实际上却是相同的。

下面是一个重写析构函数的例子:
不重写析构函数的代码:


class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成年人买票" << endl;
	}
	 ~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生买票" << endl;
	}
	 ~Student()
	{
		cout << "~Student" << endl;
	}

};

void a(Person* p)
{
	delete p;
}
int main()
{
	Person* p = new Person();
	Student* s = new Student();
	a(s);	
	return 0;
}

我们这个代码是没有没有重写析构函数的,因为析构函数不是虚函数,我们看看结果。
在这里插入图片描述

我们会发现,问题很严!因为我传过去的是一个Student,也就是基类对象。但是我们用父类指针接收,那么 指针pp的使用范围 就是Person的范围。所以无法调用子类的析构函数,只能调用自己的析构函数。也就是说!释放不彻底,因为传过去的对象是s对象的指针,而delete它时,它却只调用了父类的析构函数,没有调用自己本身的析构函数,如果此时s对象有动态开辟的空间,那么就造成了内存泄露,这是很严重的。这是因为指针是Person类型的,所以只能访问Person的那一部分。想要解决这个问题,我们就需要重写析构函数。以至于传子类对象指针,父类指针接收也能调到子类的析构函数。


正确的写法:

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成年人买票" << endl;
	}
	virtual ~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生买票" << endl;
	}
	virtual ~Student()
	{
		cout << "~Student" << endl;
	}
	 int _a;

};

void a(Person* pp)
{
	delete pp;
}
int main()
{
	Person* p = new Person();
	Student* s = new Student();
	a(s);	
	return 0;
}

这时候我们可以看到Student的析构函数也被调用了,这就意味着s对象被真正析构。所以析构函数还是很有必要被重写的。
在这里插入图片描述

🌺2.7 重载,重写(覆盖),重定义(隐藏)之间的区别

一张图概括

在这里插入图片描述


💖3. override 和 final(c++11)

override 和 final 在c++11才被引用,2个关键字的作用也很简单。
final:修饰虚函数,表示虚函数不能被重写。
override:检查派生类是否重写了虚函数,如果没重写,会报错。

final的使用:
在这里插入图片描述

override的使用:
在这里插入图片描述


💖4.抽象类

在虚函数的后面加上一个 = 0,这个函数就是纯虚,这就代表这是一个抽象类,也叫接口类,抽象类不能被实例化,派生类继承后也不能实例化出对象。除非重写其基类的纯虚函数。

代码样例:


class Person
{
public:
	virtual void Eat() = 0
	{
	}
};
class Student : public Person
{
public:

};

int main()
{
	Person p;
	Student s;
	return 0;
}

在这里插入图片描述
如果想使用,我们必须重写纯虚函数。
在这里插入图片描述
但是p依然不能实例化,想要p对象,我们可以通过指针或者引用的方式。

class Person
{
public:
	virtual void Eat() = 0
	{
	}
};
class Student : public Person
{
public:
	virtual void Eat()
	{
		cout << "吃饭" << endl;
	}
};

int main()
{

	Student s;
	Person& p = s;
	p.Eat();
	return 0;
}

这种方法已经构成了多态,因为s通过了基类的指针调用其纯虚函数。


💖 5.多态的原理

那么多态是怎么实现的呢?我们先来监视一下,非多态时,子类对象和父类对象。

class Person
{
public:
	 void BuyTicket()
	{
		 cout << "成年人买票" << endl;
	}
	 int _p;
};
class Student : public Person
{
public:
	 void BuyTicket()
	{
		cout << "学生买票" << endl;
	}
	 int _s;
};

int main()
{

	Student s;
	Person p;

	return 0;
}

这是父类对象
在这里插入图片描述


这是子类对象
在这里插入图片描述
接下来我们看看实现多态时的样子。

class Person
{
public:
	virtual void BuyTicket()
	{
		 cout << "成年人买票" << endl;
	}
	 int _p;
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生买票" << endl;
	}
	 int _s;
};

int main()
{

	Student s;
	Person p;

	return 0;
}

父类对象:
在这里插入图片描述
子类对象:
在这里插入图片描述
我们可以很清楚的发现,构成多态后,对象里面会多一个__vfptr的参数。而这个参数是虚函数表指针(简称虚表指针),它指向一个数组,数组的每个元素是一个函数指针。而这个数组,叫做虚函数表。而虚函数表里面存的就是虚函数的地址。当子类进行重写的时候,就会去虚函数表里面把存储的父类虚函数的地址覆盖成自己的虚函数地址。所以进行切片时,虚函数表里的虚函数地址还是自己的。调用虚函数时,去自己的虚函数表里面找到对应的虚函数。

所以多态的实现原理,简单来说就是以下几个步骤:

  1. 看父类有没有虚函数,如果有虚函数,则会在父类生成一个虚函数表。
    在这里插入图片描述

  2. 子类继承父类时,会把父类的虚函数表也继承下来。
    在这里插入图片描述

  3. 随后子类查找有没有符合重写条件的函数(三同,且是虚函数),符合重写条件则到继承的虚函数表里,覆盖掉父类的虚函数表。

在这里插入图片描述
此时如果构成多态,就会进Student的虚函数表里面找对应的虚函数调用,因为父类虚函数的地址被替换了。


💖 6.单继承和多继承的虚函数表

🌺 6.2 打印虚函数表

以下这段代码可以直接打印虚函数表,其原理取对象的地址,随后强制转换成一个指针。因为指针在32平台是4字节,在64平台是8字节。所以把对象强制转换成指针类型,访问的第一个元素就是一个指针的大小。因为虚表指针就是在对象的最前面4个或8个字节。然后强制转换成函数指针。

class Person
{
public:
	virtual void fun1()
	{
		cout << "Person::fun1()" << endl;
	}

	virtual void fun2()
	{
		cout << "Person::fun2()" << endl;
	}

	 int _p;
};
class Student : public Person
{
public:
	virtual void fun1()
	{
		cout << "Student::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "Student::fun2()" << endl;
	}
	virtual void fun3()
	{
		cout << "Student::fun3()" << endl;
	}
	virtual void fun4()
	{
		cout << "Student::fun4()" << endl;
	}
	 int _s;
};
void a(Person& p)
{
	p.fun1();
}

typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
	
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl << endl;
}
int main()
{
	Person p;
	Student s;

	VFPTR * vTableb = (VFPTR*)(*(void**)&p);
	PrintVTable(vTableb);
	VFPTR* vTabled = (VFPTR*)(*(void**)&s);
	PrintVTable(vTabled);
	return 0;
}

我们看看打印结果
在这里插入图片描述
这样我们就可以看到虚函数表的打印结果了。

🌺 6.3 单继承的虚函数表

上面说过的情况就是单继承时的情况,子类继承父类时会继承它的虚表。随后在虚表里面覆盖重写的函数。那么如果此时子类自己的函数也是虚函数,那么也会添加至虚函数表中。

两个类的关系如下所示

class Person
{
public:
	virtual void fun1()
	{
		cout << "Person::fun1()" << endl;
	}

	virtual void fun2()
	{
		cout << "Person::fun2()" << endl;
	}

	 int _p;
};
class Student : public Person
{
public:
	virtual void fun1()
	{
		cout << "Student::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "Student::fun2()" << endl;
	}
	virtual void fun3()
	{
		cout << "Student::fun3()" << endl;
	}
	virtual void fun4()
	{
		cout << "Student::fun4()" << endl;
	}
	 int _s;
};

我们可以发现,子类的fun1和fun2与父类构成多态。可是fun3和fun4并没有构成多态,但是它们依然会被添加进子类的虚函数表。
在这里插入图片描述
所以虚函数表也会添加自身的虚函数。

🌺 6.4 多继承的虚函数表

那么如果是多继承呢?

以下代码实现了多继承,me继承了Base1和Base2。因此,Base1的虚函数表在m的前4/8个字节的位置。但是Base2的虚函数表可不在后面。所以要想知道Base2的虚函数表位置。我们需要m的地址在原有的基础上加一个Base1大小,这样就到达了Base2对象的首地址,再取前4/8个字节就是Base2的虚函数表。

class Base1
{
public:
	virtual void fun1()
	{
		cout << "Base::fun1()" << endl;
	}

	virtual void fun2()
	{
		cout << "Base::fun2()" << endl;
	}

	 int _p;
};
class Base2
{
public:
	virtual void fun3()
	{
		cout << "Base2::fun3()" << endl;
	}
	virtual void fun4()
	{
		cout << "Base2::fun4()" << endl;
	}

};

class me :public Base1,public Base2
{
	virtual void fun1()
	{
		cout << "me::fun1()" << endl;
	}
	virtual void fun2()
	{
		cout << "me::fun2()" << endl;
	}
	virtual void fun5()
	{
		cout << "me::fun5()" << endl;
	}
	virtual void fun6()
	{
		cout << "me::fun6()" << endl;
	}
};


typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
	
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i+1, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl << endl;
}
int main()
{
	me m;

	VFPTR * vTableb = (VFPTR*)(*(void**)&m);
	PrintVTable(vTableb);
	VFPTR* vTabled = (VFPTR*)(*(void**)((char*)&m+sizeof(Base1)));
	PrintVTable(vTabled);
	return 0;
}

我们看看代码结果:
在这里插入图片描述
我们可以看到,当有一个类继承了多个类时,那么会产生多张虚函数表。而自己的虚函数(非重写) 将会被默认放在第一张函数表中。

💖 7.多态面试问答题

  1. 什么是多态?
    2答:不同的事物做同一行为产生不同的结果。

  2. 什么是重载、重写(覆盖)、重定义(隐藏)?
    答:重载要在同一作用域下,且函数名相同,但参数的顺序,个数,类型不同。
    重写是当基类和派生类有一模一样的虚函数时,子类虚函数表中的父类虚函数地址会被覆盖。
    重定义,从父类继承下来,且没有重写的就是重定义,重定义函数名,参数相同。

  3. 多态的实现原理?
    答:父类的所有虚函数都会存在虚函数表中,而虚函数表存储在常量区。当子类继承了父类时,也会继承父类的虚函数表,如果此时子类又符合重写要求的函数。则会去自己的虚函数表中替换掉父类的虚函数地址,换成自己的虚函数地址。

  4. inline函数可以是虚函数吗?
    答:可以,不过编译器就忽略inline属性,这个函数就不再是
    inline,因为虚函数要放到虚表中去。

  5. 静态成员可以是虚函数吗?
    答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。

  6. 构造函数可以是虚函数吗?
    答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。

  7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
    可以,并且最好把基类的析构函数定义成虚函数,否则当子类和父类构成多态时。delete释放对象可能会导致内存泄漏,具体上面有讲解。

  8. 对象访问普通函数快还是虚函数更快?
    答:构成多态,普通函数快。不构成多态,一样快。

  9. 虚函数表是在什么阶段生成的,存在哪的?
    答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

  10. 什么是抽象类?抽象类的作用?
    答:抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。

总结🥳:

💦💦如果有写的有什么不好的地方,希望大家指证出来,我会不断的改正自己的错误。💯💯如果感觉写的还可以,可以点赞三连一波哦~🍸🍸后续会持续为大家更新C/C++,数据结构,Linux相关的知识

🔥🔥你们的支持是我最大的动力,希望在往后的日子里,我们大家一起进步!!!
🔥🔥

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/171783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SIMD性能优化

文章目录前言MMXSSEAVX使用内置函数使用SSE/AVX命名规则SSE/AVX操作类别实战汇编使用优化前代码详解优化后代码详解引用文章#mermaid-svg-sNu7iEVk2jpyjjtX {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#fff;}#mermaid-svg-sNu7iEVk2…

【黑马学成在线2023版】解决P7创建父工程时Maven的pom文件依赖爆红问题(亲测有效)

本期目录问题描述解决方案问题描述 感谢黑马贡献的高质量视频教程《学成在线》微服务项目。笔者在学到《P7-创建父工程基础工程》时&#xff0c;直接粘贴黑马老师的 pom 文件中的依赖会出现依赖的 <artifactId> 和 <version> 爆红&#xff0c;显示无法找到依赖的错…

vite学习笔记

vite 1.vite是什么&#xff1f; 基于ES-Module的前端构建工具 2.为什么选择vite&#xff1f; 在浏览器支持 ES 模块之前&#xff0c;JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。 缓慢的服务器启动 当冷启动开发服务器时&#xff0c;基于打包器的方式…

YOLOv8来啦!YOLO内卷期模型怎么选?9+款AI硬件如何快速部署?深度解析

在这新春佳节到来之际&#xff0c;回顾整个虎年&#xff0c;堪称YOLO内卷元年&#xff0c;各路YOLO系列神仙打架&#xff0c;各显神通。一开始大部分用户做项目做实验还是使用的YOLOv5&#xff0c;然后YOLOv6、YOLOv7、PP-YOLOE、DAMO-YOLO、RTMDet就接踵而至&#xff0c;于是就…

SpringBoot原理解析

目录 一、Profile功能 &#xff08;一&#xff09;、application-profile功能 &#xff08;二&#xff09;、Profile条件装配功能 &#xff08;三&#xff09;、profile分组 二、外部化配置 &#xff08;一&#xff09;、外部配置源 &#xff08;二&#xff09;、配置文…

消息队列 ---nsq

设计 topic和channel 单个nsqd实例旨在一次处理多个数据流。流称为“主题”&#xff0c;一个主题有 1 个或多个“通道”。每个通道都会收到一个主题的所有消息的_副本_。 主题和通道道_不是_提前配置的。主题是在首次使用时通过发布到指定主题或订阅指定主题的通道来创建的。…

如何看懂行业分析报告?

从下面几部分聊聊行业分析&#xff1a;1.什么时候需要做行业分析&#xff1f;2.如何做行业分析&#xff1f;3.案例学习4.在工作中如何应用&#xff1f;5.在生活中如何应用&#xff1f;1.什么时候需要做行业分析呢&#xff1f;当你在对自己进行职业规划的时候&#xff0c;会思考…

【SpringCloud15】SpringCloud Stream消息驱动

1.消息驱动概述 1.1 为什么要引入消息驱动 1.2 是什么 概述&#xff1a;屏蔽底层消息中间件的差异&#xff0c;降低切换成本&#xff0c;统一消息的编程模型 官网 Spring Cloud Stream是用于构建与共享消息传递系统连接的高度可伸缩的事件驱动微服务框架&#xff0c;该框架提…

一 、Qml开发之环境搭建

进入官网下载相应版本的qtcreator &#xff1a;https://download.qt.io/archive/qt/5.12/5.12.6/ 1.1 安装的时候注意如下对话框&#xff0c;需要选择下图所示的必须选项&#xff0c;因为我是mac 所以选择的macOS下载完之后进行点击安装&#xff0c;安装后运行软件图片如下&…

小程序uni-app介绍

uni-app介绍 uni-app简介 uni-app 是一个使用**Vue.js **开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序&#xff08;微信/支付宝/百度/头条/QQ/钉钉/淘宝&#xff09;、快应用…

C++|读写xml文件开源库tingxml2的使用

参考&#xff1a; TinyXML使用方法[通俗易懂] https://cloud.tencent.com/developer/article/2037579 TinyXML2 入门教程&#xff08;这篇写很好&#xff0c;本文侧重讲解使用不过做多介绍&#xff09; 不了解xml的建议自行查阅&#xff0c;在此不赘述。 开源库github链接&…

Python中的列表、元组、字典

​​​​​​​列表是一种让程序员在代码中批量表示/保存数据的方式&#xff0c;元组和列表相比&#xff0c;是非常相似的&#xff0c;只是列表中放哪些元素可以修改调整&#xff0c;元组中放的元素是创建元组的时候就设定好的&#xff0c;不能修改调整。 列表和元组类似于其他…

SpringBoot 2-9-2 ServletAPI

使用27个解析器中 ServletRequestMethodArgumentResolver Step1 页面请求 注意RestController ResponseBody Controller Controller 将当前修饰的类注入SpringBoot IOC容器&#xff0c;使得从该类启动后就被实例化 ResponseBody 表示它会以Json字符串的形式返回给客户…

【日常系列】LeetCode《27·动态规划2》

数据规模->时间复杂度 <10^4 &#x1f62e;(n^2) <10^7:o(nlogn) <10^8:o(n) 10^8<:o(logn),o(1) 内容 1&#xff09;爬楼梯、打家劫舍问题 2&#xff09;0-1&#xff0c;多重&#xff0c;完全&#xff0c;二维被动背包问题 lc 70【剑指 10 - 2】【top100】&…

Maven仓库集成与使用

1.概念:Maven主要服务于基于java平台的项目构建(编译、测试、生成文档、打包、部署等)&#xff0c;依赖管理和项目信息管理。 2.四大特性: 2.1:依赖管理系统(jar包管理, jar 升级时修改配置文件即可) 依赖(Coordination):由groupId、artifactId、version组成 …

PHP MySQL 预处理语句

预处理语句对于防止 MySQL 注入是非常有用的。 预处理语句及绑定参数 预处理语句用于执行多个相同的 SQL 语句&#xff0c;并且执行效率更高。 预处理语句的工作原理如下&#xff1a; 预处理&#xff1a;创建 SQL 语句模板并发送到数据库。预留的值使用参数 "?" 标…

Python 实现 JSON 解析器

Json 解析 文章目录Json 解析Json 的组成对象结构数组结构词法分析逻辑性解析解析对象类型解析数组类型完整代码小结Json 的组成 JSON结构共有2种 对象结构数组结构 一个合法的JSON字符串可以包含这几种元素: 特殊符号,如"{" “}“表示一个JSON Object&#xff0…

将DataFrame进行转置的DataFrame.transpose()方法

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 将DataFrame进行转置 DataFrame.transpose() 选择题 关于以下python代码说法错误的一项是? import pandas as pd dfpd.DataFrame({a:[a1,a2],b:[b1,b2]}) print("【显示】df:\n"…

高德地图红绿灯读秒是怎么实现的?(一)

关于这个读秒实现功能众说风云&#xff0c;目前有两种说法&#xff0c;一种说是靠大数据分析&#xff0c;一种说是靠交管部门数据。 我们先看一下官方的回应&#xff1a;可以自行去抖音看官方号的解释。 以下为原答&#xff1a; 有人说是接入了地方交管数据&#xff0c;其实政策…

2022年度 FinClip 扩展 SDK 推荐!

2022年&#xff0c;FinClip 团队进行了24个产品迭代&#xff0c;为了丰富FinClip 的平台能力&#xff0c;除了核心SDK之外&#xff0c;我们还为开发者们提供了扩展SDK&#xff0c;扩展SDK是一个依赖核心SDK的库&#xff0c;里面提供了核心SDK中所没有的各种小程序API。 官方希…