C++进阶-->多态(Polymorphism)
1. 多态的概念
多态,顾名思义多种形态;多态分为编译时多态(静态多态)和运行时多态(动态多态),静态多态就是就是我们前面讲的函数重载和函数模板,可以通过传不同类型,然后在编译期间就确定好使用哪个的称为静态多态;动态多态就是一般在程序运行时确定使用指定函数的就称作动态多态。
动态多态,举个生活中的例子,例如铁路12306中买票的时候,当成年且没有特殊情况的人是全价买票,学生买票有特定的学生价买票,军人买票时可以优先买票,而这个过程抽象来说就是传入不同的对象就会完成不同的行为。
2. 多态的定义及使用
2.1 多态的构成条件
多态是一个继承关系下的类对象,去调用同一函数,产生了不同行为。比如Student继承了Person。Person对象买票全价,Student对象优惠买票。
2.1.1 实现多态的两个必要条件
1. 必须是基类的指针或引用调用函数。
2. 被调用的函数必须是虚函数(virtual修饰的)。
解释一下为什么必须是基类的指针或引用调用函数,因为只有是基类的指针或引用才能做到既接收基类的地址又接收派生类的地址;
第二派生类必须对基类的虚函数进行重写/覆盖,派生类对基类的虚函数重写了才能做到传入不同的类执行不同的操作的效果,才能达到多态。
3. 虚函数
在继承那一章我们也讲到虚继承,虚继承是用来避免菱形继承带来代码的冗余和二义性的问题,关键字是virtual;虚函数的关键字也是virtual,只需要在类成员函数前面加上virtual修饰,那么这个成员函数就被称为虚函数。注意:如果不是成员函数是不能加virtual修饰的。如下代码所示:
class Person
{
public:virtual void BuyTicket(){cout << "买票--全价" << endl;}
};
3.1 虚函数的重写
派生类中有一个虚函数跟基类的虚函数完全一致的虚函数称为虚函数的重写。
完全一致指的是:函数名、参数列表、返回值类型。(注意!这里的参数列表指的是参数的类型,名字相同没有关系);如下代码所示:
class Person
{
public:virtual void BuyTicket(int x){cout << "买票--全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(int y){cout << "买票--半价" << endl;}
};
还有一点需要提一下的是:如果基类的虚函数加了virtual,那么派生类的虚函数可以不加virtual,这里我们可以直接按“在继承的时候就把基类的虚函数的属性继承下来了”理解,这个不太建议这样子用,但在笔试或者考试选择题可能会考。
下面是虚函数、虚函数的重写、实现多态的代码使用:
#include<iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(int x){cout << "买票--全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(int y){cout << "买票--半价" << endl;}
};void BuyTicket(Person* ptr)
{ptr->BuyTicket(6);
}int main()
{Person p1;Student s1;BuyTicket(&p1);BuyTicket(&s1);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
最后再补充一个重重重重要的知识:虚函数的重写本质上是重写虚函数的实现!!!!下面就有一道相关的练习题。(毕竟人教人教不会,事教人一脚就懂)
3.2 多态场景下的选择题

#include<iostream>
using namespace std;class A
{
public:virtual void func(int val = 1){std::cout << "A->" << val << std::endl; }virtual void test() {func(); }
};class B : public A
{
public:void func(int val = 0) {std::cout << "B->" << val << std::endl; }
};int main(int argc, char* argv[])
{B* p = new B;p->test();return 0;
}
首先如果我们对重写的知识理解的不够全面的话,那么肯定会选择D, 但我们上面说了重写是重写函数的实现,函数的参数不会有变化,所以我们调用的func应该如下图所示:
所以正确的答案是B;
最后我们再来细致分析一下p->test()究竟怎么构成多态,我们都知道在类里面的成员函数会有一个隐藏的参数就是this指针,而test是在A里面的,所以完整的函数应该为 virtual void test(A* this)这里就构成重载了,基类做this指针的返回值,完全符合上面所说的2个多态的构成条件。
3.3 虚函数重写的一些问题
3.3.1 协变
协变:派生类重写基类的虚函数的时候返回值不同。即基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或引用,这个就成为协变。协变用处不多,仅需了解一下即可;如下代码所示:
#include<iostream>
using namespace std;
//基类
class A
{};
//派生类
class B :public A
{};class Person
{
public://基类做返回值virtual A* BuyTicket()//virtual Person* BuyTicket()也可以{cout << "买票-全价" << endl;return nullptr;}
};class Student :public Person
{
public://派生类做返回值virtual B* BuyTicket()//virtual Student* BuyTicket()也可以{cout << "买票-打折" << endl;return nullptr;}
};void Func(Person* ptr)
{ptr->BuyTicket();
}
3.3.2 析构函数的重写
基类的析构函数只要定义为虚函数后,无论派生类的析构函数写不写virtual,都会与基类的析构函数构成重写。
前面继承有提到一嘴,就是析构函数最后都会被编译器转换为destructor(),那么这时候函数名(destructor)相同构成隐藏(这个是继承那边的)。
所以只要基类的析构加了virtual,派生类的就构成了重写;函数名一样都是destructor、参数一样都没有、返回值类型都没有。
接下来就谈谈为什么要实现析构函数的重写(重点!!!!!)
在思考为什么的时候我们就可以通过反推来证明即如果没有实现会发生什么情况;我们先看一下下面的代码:
#include<iostream>
using namespace std;class A
{
public:~A(){cout << "~A()" << endl;}
};class B :public A
{
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0;
}
对代码的解析:我们用基类A指针实例化了两个对象p1接收基类A的地址、p2接收派生类B的地址,如果说我们没有实现析构函数的重写,delete p1和delete p2 是直接走基类的析构函数,因为没有构成多态,也就不会产生不同的行为而只走基类A的析构函数,如果派生类B里面有动态申请空间,那么就会出现内存泄漏。如下图:

但如果我们让基类A的析构函数定义为虚函数,那么就构成多态,从而会根据传入不同的类型调用不同对象的析构函数。
这里为什么会还会调用一次基类A的析构函数呢?前面继承有提到,派生类的析构函数里面其实包含了基类的析构函数,主要是为了达到先子后父的析构顺序。
最后总结一下:C++的这些实现其实都是有关联的,他们为了处理上面的这种情况而实现了析构函数的重写,为了实现析构函数的重写而实现了让析构函数无论函数名为什么最后都会转换成destructor。真的很妙。
3.3.3 override和final关键字
override是用来检查你是否重写错误;
final在继承那里也提到过,如果想实现一个类不被继承就用final,而这里的final是用来实现一个不能被重写的虚函数;
3.3.4 重载/重写/隐藏的对比
3.3.5 纯虚函数和抽象类
纯虚函数只要在虚函数后面加个“=0”即可;纯虚函数不需要定义实现,没有很大作用,但是在语法上是支持实现的 。而包含纯虚函数的类称作抽象类,抽象类不能实例化出对象,但是派生类可以继承抽象类,如果派生类不重写纯虚函数,那么派生类也称为抽象类。纯虚函数其实就是间接强制了派生类要重写纯虚函数。

干说有点难理解,举个实际例子,狗和猫都是动物,狗的叫声是“旺旺”,猫的叫声是“喵喵”,狗和猫都是动物都有动物的特征所以可以继承动物的属性,但是动物是怎么叫的我们不知道,而动物就可以称作是抽象类,“叫”这一个动作可以用纯虚函数实现,猫和狗则是动物的派生类,所以需要重写这个纯虚函数,狗重写“叫”函数为“旺旺”,猫则是“喵喵”。如下代码所示:
#include<iostream>class Animal
{
public:virtual void talk() = 0;
};
class Dog : public Animal
{
public:virtual void talk() {std::cout << "汪汪" << std::endl;}
};class Cat : public Animal
{
public:virtual void talk() {std::cout << "(>^ω^<)喵" << std::endl;}
};
int main()
{Cat cat;Dog dog;cat.talk();dog.talk();return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
4. 多态的原理
首先我们看一道题,用一道题引出下面的知识;
#include<iostream>
using namespace std;class Base {
public:virtual void Func1(){cout << "Func1()" << endl;}
protected:int _b = 1;char _ch = 'x';
};int main()
{Base b;cout << sizeof(b) << endl;return 0;
}
我们知道,计算类的大小是使用对齐的方式计算的,那么计算出来的结果是8,可能我们的答案就选择了C,实则并不然,答案选的是D,首先我们通过调试看一下里面究竟多了什么玩意,如下图所示:

我们发现里面除了成员变量_b和_char,还有一个指针_vfptr,这个指针我们称作虚函数表指针(virtual function pointer);
4.1 虚函数表指针
每个含有虚函数的类中都会有一个虚函数表指针,这个指针是指向一块存着虚函数地址的空间,虚函数表也称作虚表。所以我们传入不同类去调用该类的虚函数的时候就是通过这个指针去调用不同类中的虚函数的。
4.2 多态的实现
首先我们先来看一串代码
#include<iostream>
using namespace std;
class Person {
public:virtual void BuyTicket() {cout << "买票-全价" << endl;}
protected:string _name;
};class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-打折" << endl;}protected:int _id;
};class Soldier : public Person
{
public:virtual void BuyTicket() { cout << "买票-优先" << endl;}protected:string _codename; // 代号
};void Func(Person* ptr)
{// 这里可以看到虽然都是Person指针Ptr在调用BuyTicket// 但是跟ptr没关系,而是由ptr指向的对象决定的。ptr->BuyTicket();
}int main()
{// 其次多态不仅仅发生在派生类对象之间,多个派生类继承基类,重写虚函数后// 多态也会发生在多个派生类之间。Student st;Soldier sr;Person pr;Func(&st);Func(&sr);Func(&pr);return 0;
}
我们可能会好奇,Func内的ptr是如何做到传入不同的类对象的地址然后走不同的虚函数执行不同操作的,下面就来讲解一下。
如下图


通过上图我们看到,满足多态后,不再是编译时通过调用对象确定函数的地址,而是运行时到指定的对象内找到对象的__vfptr(虚表)然后确定对应的虚函数的地址,然后执行,这样就实现了指针或引用指向基类就调用基类的虚函数,指向派生类就调用派生类的虚函数。
4.3 动态绑定和静态绑定
动态绑定则是满足多态条件的函数调用是在运行时绑定,说简单点就是在运行时去虚函数表内找到函数地址然后调用。
静态绑定则是在编译期间确定函数的地址然后调用。
这个可以通过汇编层看一下。
动态绑定👇看着挺复杂的,挺多操作,因为他是在运行时先去找到__vptr这个指针,然后找到虚函数表,再通过虚函数表内的地址找到虚函数,再调用该函数。

静态绑定👇编译器直接确定函数地址然后直接调用。

4.4 虚函数表
1. 基类对象的虚函数表中存放基类所有虚函数的地址。如果同类型实例化对象的话则虚表共用,不同类型虚表各自独立。
2. 派生类由两部分构成,一部分是派生类自己的成员,还有一部分是基类的成员,还有基类的虚表指针,继承下来的基类中有虚表指针的话就不会自己再生成一个新的虚表指针,但继承下来的和虚表指针和基类的虚表指针不是同一个,他们的地址不同。
3. 如果派生类中有虚函数的重写,那派生类虚表中对应的虚函数就会被重写的虚函数覆盖。
4. 派生类的虚表中包含基类的虚函数地址,派生类重写的虚函数地址,还有派生类自己的虚函数地址。
5. 虚函数的本质是一个存虚函数指针的指针数组,一般情况下这个数组后面会放一个0x00000000的标记 (这个C++并没有进⾏规定,各个编译器自行定义的,vs系列编译器会再后面放个0x00000000 标记,g++系列编译不会放)
6. 虚函数和普通函数一样,编译好后是一段指令,都是存在代码段的,只是虚函数的地址存在虚表中。
7. 虚函数表的存在位置是看编译器的,C++并没有规定,下面将来看一下vs的在哪里;如下代码所示:
#include<iostream>
using namespace std;class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }void func5() { cout << "Base::func5" << endl; }
protected:int a = 1;
};class Derive : public Base
{
public:// 重写基类的func1virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func1" << endl; }void func4() { cout << "Derive::func4" << endl; }protected:int b = 2;
};int main()
{int i = 0;static int j = 1;int* p1 = new int;const char* p2 = "xxxxxxxx";printf("栈:%p\n", &i);printf("静态区:%p\n", &j);printf("堆:%p\n", p1);printf("常量区:%p\n", p2);Base b;Derive d;Base* p3 = &b;Derive* p4 = &d;printf("Person虚表地址:%p\n", *(int*)p3);printf("Student虚表地址:%p\n", *(int*)p4);printf("虚函数地址:%p\n", &Base::func1);printf("普通函数地址:%p\n", &Base::func5);return 0;
}
👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇
运行结果为:
由此可见,虚表的地址与常量区的地址很接近,那就说明虚表是存在常量区的。
5. 额外补充的知识
首先我们先来看一个代码。
#include<iostream>
using namespace std;class A
{
public: void test(float a) {cout << a; }
}; class B :public A
{
public: void test(int b) {cout << b; }
}; void main()
{A* a = new A;B* b = new B;a = b; a->test(1.1);
} 按照我们的思路,a赋值给b之后,那么A的test的隐式指针this就应该转换成B类的this,应该调用的是cout出b的值为1。
但是编译器不是这么做的,编译器虽然通过指针的指向访问成员变量,但是不能通过指针的指向来访问成员函数,而是通过指针的类型来访问成员函数。也就是说无论a的指针指向了谁,都只能访问到A类内的成员函数。(引用也是一样的!!!!!)
派生类有几个父类,且那几个父类都有虚函数的话,那么派生类就会有几张虚表。而如果在派生类中增加虚函数的话只会放在第一张虚表的最后。
相关文章:
C++进阶-->多态(Polymorphism)
1. 多态的概念 多态,顾名思义多种形态;多态分为编译时多态(静态多态)和运行时多态(动态多态),静态多态就是就是我们前面讲的函数重载和函数模板,可以通过传不同类型,然后…...
python实战项目51:selenium结合requests获取某众点评评论
python实战项目51:selenium结合requests获取某众点评评论 一、selenium获取cookies二、利用requests发送请求三、注意事项四、完整代码一、selenium获取cookies 首先,初始化selenium的webdriver,然后使用webdriver打开某众点评主页,之后手动扫码登录,利用selenium的get_c…...
面试准备第一版ssm spring-springmvc
请写出spring中常用的依赖注入方法: 1、setter 2、构造方法注入 3、字段注入 Setter 注入: 通过公共的 setter 方法进行依赖注入。优点:可选依赖,能更清晰地看到依赖关系。缺点:依赖在构造时不可用,可能导…...
Ubuntu学习笔记 - Day1
文章目录 学习目标:学习内容:学习笔记:Linux简介基于Linux内核的系统 Ubuntu简介GNU简介 远程连接Ubuntu查看Ubuntu的IP地址Mac连接Ubuntu此时可能显示报错,连接被拒绝解决办法连接成功连接退出 学习目标: 一周掌握 Li…...
挑战Java面试题复习第4天,坚持就是胜利
挑战第 4 天 Excption与Error包结构OOM 知识点SOF 知识点线程程序进程知识点有些字段不想序列化,怎么办?说说 IO 流Java IO与 NIO的区别 Excption与Error包结构 运行时异常(RuntimeException): 包括RuntimeException…...
Android 虚拟化框架(AVF)指南
Android 虚拟化框架(AVF)指南 一、项目介绍二、项目特色三、如何使用AVF四、总结 随着移动设备的普及和应用场景的多样化,安全性和隐私保护成为了移动操作系统的重要课题。Android作为全球最广泛使用的移动操作系统之一,一直在不断…...
day-77 超级饮料的最大强化能量
思路 动态规划:因为每一步要么选A,要么选B,所以问题可以转换为求最后一步从A选或从B选中的较大值 解题过程 定义而二维数组dp,dp[i][0]表示最后一步从A取能获得的最大能量,dp[i][1]表示最后一步从B取能获得的最大能量状态转换方程…...
有道小P 1.0.8 | 完全免费的AI全科学习助手,家长的好帮手
有道小P是一款由网易有道出品的完全免费的AI全科学习助手,专为中小学生设计。它支持多种输入方式,包括文字、语音和拍照识别,能够覆盖十个科目的所有题型,提供详细的解析和逐步解答,帮助孩子们理解和吸收知识。此外&am…...
vue项目中如何在路由变化时增加一个进度条
在 Vue.js 项目中,使用路由(如 Vue Router)时,为了提升用户体验,你可能会想要在路由变化时显示一个进度条。这可以通过多种方式实现,其中一种流行的做法是使用第三方库,如 vue-loading-bar 或 n…...
如何解决mingw64安装后配置完环境变量仍然执行不了gcc命令以及Vscode中的环境路径配置中找不到gcc
配置环境变量教程很多,就不多说,说下耗费一小时解决的问题:mingw64安装后配置完环境变量仍然执行不了gcc命令 配置 了N次了,都还是在终端找不到指令,然后,将路径放到第一个,然后再看下…...
3-petalinux2018.3 摸索记录 - 命令驱动 _ 交叉编译链
一、命令行控制GPIO 对于ps端设备,在板卡的linux系统中,切换到/sys/class/gpio路径下可以看到目前挂载的gpio设备。 export: 导入用户空间 gpiochip: 系统中gpio寄存器信息 unexport: 移除用户空间 以MIO40和MIO42…...
【二分查找】——模板
二分查找模板题 一、题目要求 给定一个长度为n的非递减数组和一个数字target,要求找到数组中第一个大于等于target的位置pos,数组下标从 0 开始。如果不存在大于等于target的数字,则输出 -1。 二、输入格式 第一行:为两个正整…...
从可逆计算看DSL的设计要点
低代码平台的可视化设计器本质上是DSL(Domain Specific Language)的结构化编辑器。可视化设计器将编辑的结果序列化成文本格式时所采用的规范就是一种DSL语法定义。 Nop平台基于可逆计算原理,提出了一整套系统化的构建机制来简化DSL的设计和…...
axios竟态问题
竟态问题 在我们日常开发经常遇到一些竟态问题 例子1 现象1 表格分页,如果设置请求loading, 先切换到分页第99页,迅速在又切换到第1页,最后列表显示的是第99页数据。 原因 由于第99页请求数据花费时间可能500ms,第1页数据只需要100ms,第1页…...
如何批量注册多个Outlook邮箱账号并避免关联
批量注册多个Outlook邮箱账号时,如何避免账号之间的关联性是一个重要的考量因素。会在此文一起探讨如何高效且安全地批量注册多个Outlook邮箱账号,并提供一些实用的建议来确保这些账号不会被关联。 一、Outlook邮箱批量注册机制 在深入注册流程之前&…...
如何在安卓設備上設置全局代理?
對於安卓用戶來說,設置全局代理是維護網路隱私一種有效的方法,可以幫助在所有應用中使用同一個代理伺服器。本文將詳細介紹如何在安卓設備上進行全局代理設置。全局代理指的是通過一個代理伺服器來轉發設備上所有應用程式的網路請求。這樣,所…...
操作系统实验记录
实验零:虚拟机安装 一、安装vmware虚拟机 与vmware匹配搜索结果 - 考拉软件 (rjctx.com),下载17.5.1版本即可下载后对照教程安装 二、下载iso虚拟驱动 搜索清华大学镜像网站,点击再搜ubuntu,下载这个4.1GB的iso文件安装后打开vmware虚拟机 三、配置vmware虚拟机 右键管…...
FastAPI 路径参数详解:动态路径与数据校验的灵活实现
FastAPI 路径参数详解:动态路径与数据校验的灵活实现 本文全面介绍了在 FastAPI 中使用路径参数的技巧和实现方式。路径参数允许 API 动态响应不同路径中的请求信息,结合 URL(Uniform Resource Locator)和 URI(Unifor…...
【STM32】SD卡
(一)常用卡的认识 在学习这个内容之前,作为生活小白的我对于SD卡、TF卡、SIM卡毫无了解,晕头转向。 SD卡:Secure Digital Card的英文缩写,直译就是“安全数字卡”。一般用于大一些的电子设备比如:电脑、数码相机、AV…...
我一口气记录下整个接口自动化测试过程!
一、为什么选用postman postman调试工具无论对于开发和测试小白,还是技术大牛来说应该都耳熟能详,在过去的几年里大家对这款工具应用最广的用途是把当作接口调试的测试工具,它能发送几乎所有类型的HTTP请求,操作界面非常简洁美观…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
