【C++】多态:编程中的“一人千面”艺术
目录
- 一、多态的概念
- 二、多态的定义及实现
- 1.多态的构成条件
- 2.虚函数的重写
- 2.1 什么是虚函数?
- 2.2 虚函数的重写是什么?
- 2.3 虚函数重写的两个例外
- 2.4 C++11 override 和 final
- 2.5 重载、覆盖(重写)、隐藏(重定义)的对比
- 三、抽象类
- 3.1 概念
- 3.2 接口继承和实现继承
- 多态绝命题(超级坑)
- 四、多态的原理
- 4.1虚函数表
- 4.2多态的原理
- 4.2.1虚基表和虚表
- 五、单继承和多继承关系的虚函数表
- 5.1 单继承中的虚函数表
- 5.2 多继承中的虚函数表
- 没有彩蛋有🥚
前言
本篇我们来探索一下C++中的多态,欢迎大家和我一起走进多态
一、多态的概念
概念:通俗说就是多种形态,不同的对象去完成同一件事情,会产生不同的结果(多种形态)
例子:就拿买火车票来说,普通人和学生还有军人买票是不一样,普通人买票是全价,学生买票是半价,军人买票是优先买,他们都是人,但是买票的结果不一样,体现了不同的对象干同一件事结果不同
二、多态的定义及实现
1.多态的构成条件
构成多态还有两个条件:
① 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
② 必须通过基类的指针或者引用调用虚函数
2.虚函数的重写
2.1 什么是虚函数?
虚函数:即被virtual修饰的类成员函数称为虚函数
class Person
{
public://被virtual修饰的函数叫虚函数virtual void BuyTicke(){cout << "Person->全价" << endl;}
};
2.2 虚函数的重写是什么?
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
代码演示:
class Person
{
public:virtual void BuyTicket(){cout<<"Person->"<<"买票-全价"<<endl;}
};
class Student:public Person
{
public://void BuyTicket()virtual void BuyTicket(){cout<<"Student->"<<"买票-全价"<<endl;}
};
void func(Person& p)//构成多态的必要条件之一,在调用虚函数时必须通过基类的指针或者引用来调用
{p.BuyTicket();
}
int main()
{Person p;Student s;func(p);func(s);return 0;
}

总结:传Person的对象就调用Person类的虚函数,传Student对象就调用Student类的虚函数,传谁的对象就调用谁对应的虚函数,指向谁调用谁的虚函数
2.3 虚函数重写的两个例外
1.协变
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A
{};
class B :public A
{};
class Person
{
public:virtual A* BuyTicke(){cout << "Person->全价" << endl;}
};
class Student :public Person
{
public:virtual B* BuyTicke(){cout << "Student->半价" << endl;}
};
注意:协变时,相对应的虚函数的返回值要么是指针要么是引用,不能一个指针一个引用,这样不匹配,另外,返回类型要是一对父子类
2.析构函数的重写
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。看起来违背了重写的则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor这里也和前面讲的继承那块的析构函数隐藏相呼应
class Person
{
public:~Person() { cout << "~Person()" << endl; }
};
class Student :public Person
{
public:~Student() { cout << "~Student()" << endl; delete[] _ptr;}
private:int* _ptr=new int[10];
};
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}
大家看一下上面的代码有什么问题?

我们的期望是:指向父类调用父类析构,指向子类调用子类析构,这不就正是多态的特性嘛
我们直接让析构函数变成虚函数就可以实现我们的期望了
class Person
{
public://destructor()virtual ~Person() { cout << "~Person()" << endl; }
};
class Student :public Person
{
public://destructor()virtual ~Student() { cout << "~Student()" << endl; delete[] _ptr;}
private:int* _ptr=new int[10];
};
总结:建议析构函数写成虚函数,防止发生内存泄漏的问题
其实除了上面的两个例外还有第三个例外,就是派生类的虚函数可以不加virtual,它也构成重写,但是我们不建议不加,一般还是加上
为什么派生类的虚函数可以不加virtual呢?
正所谓重写,子类是继承了对应虚函数的声明(函数名参数返回值等),只是重写了它的定义,所以不加也可以
2.4 C++11 override 和 final
C++11提供了override和final两个关键字,可以帮助用户检测是否重写。
1.final:修饰虚函数,表示该虚函数不能被重写
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() { cout << "Benz-舒适" << endl; }//这里直接报红色波浪线了,上面已经用final修饰虚函数了,所以他不能被重写
};
final修饰类,叫最终类,表示该类不能被继承(了解一下)
2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
class Car {
public:virtual void Drive() {}
};
class Benz :public Car {
public:virtual void Drive() override { cout << "Benz-舒适" << endl; }
};
通俗来说,这个override就是用来帮助我们检查这个虚函数有没有被正确重写
2.5 重载、覆盖(重写)、隐藏(重定义)的对比


三、抽象类
3.1 概念
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car//包含纯虚函数的类就是抽象类--抽象类不能被实例化
{
public:virtual void Drive() = 0;//纯虚函数
};

3.2 接口继承和实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。
多态绝命题(超级坑)
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;
}//A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确
首先说一下,这道题选B,大家一定觉得很奇怪,我做的时候选的D,我们现在一起来看一下他为什么选B

四、多态的原理
4.1虚函数表
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
int main()
{cout<<sizeof(Base)<<endl;return 0;
}
这里计算Base类的大小,在x86的环境下计算的结果是8,在我们之前的认知里,类对象模型中,类不应该是把成员函数放在了公共的代码段区域吗,成员函数不在类里面,不参与计算,只需要计算他的成员变量就行,按理来说的话这里的计算结果应该是4
那我们就来看一下他为什么会计算出8这个结果来

通过调试我们可以看到,b对象里面不仅有成员
_b而且还有一个_vfptr,这个_vfptr又是干什么的呢?b对象中的这个_vfptr我们叫做虚函数表指针(v代表virtual,f代表function),在x86环境下,一个指针的大小是4个字节,加上这个int类型的_b,算上内存对齐的规则,它们刚好是8个字节
一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表
4.2多态的原理
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
protected:int _p = 1;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
protected:int _s = 2;
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}
多态调用:
运行时,到指向对象的虚表中找虚函数调用,指向父类调用父类的虚函数,指向子类调用子类的虚函数

我们转到反汇编看

call eax中存虚函数的指针。这里可以看出满足多态的调用,不是在编译时确定的,是运行起来
以后到对象的中取找的。
普通调用:
编译时,调用对象是那个类型,就调用它的函数

普通调用在编译链接时就已经确定了call地址,首先BuyTicket虽然是虚函数,但是Mike是对象,不满足多态的条件,所以这里是普通函数的调用转换成地址时,是在编译时已经从符号表确认了函数的地址,直接call地址
4.2.1虚基表和虚表
虚表(虚函数表):存放的是虚函数的地址,目标是实现多态
虚基表:存的是当前位置距离虚基类部分的偏移量,目的是为了解决菱形继承数据冗余和二义性问题
注意:
问题1:虚函数存在哪?
虚函数和普通函数一样,都是存在代码段(常量区)中,不是存在虚函数表中,虚函数表中仅仅存放的是虚函数的地址
问题2:虚表存在那个区域?
我们打印出各个区域的数据地址来观察一下,虚表和那个区域的地址最接近,以此来判断虚表存在那个区域
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
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);Person p;Student s;Person* p3 = &p;Student* p4 = &s;printf("Person虚表地址:%p\n", *(int*)&p);printf("Student虚表地址:%p\n", *(int*)&s);return 0;
}
在vs x86的环境下,_vfptr存放在头四个字节的位置, 我们取出Person类对象的地址,然后将其强制转化为int类型,再解引用得到头四个字节的数据,就可以取到第一个元素的地址,也就是虚函数表指针的地址

根据运行得到的结果可知,虚函数表的地址和常量区最接近,所以虚函数表应该存放在常量区
五、单继承和多继承关系的虚函数表
5.1 单继承中的虚函数表
以下面的代码为例来进行分析:
class Base
{
public:virtual void Func1(){ cout << "Base::Func1()" << endl; }virtual void Func2(){ cout << "Base::Func2()" << endl; }void Func3(){ cout << "Base::Func3()" << endl; }
private:int _b = 1;
};
class Derive : public Base
{
public:virtual void Func1(){ cout << "Derive::Func1()" << endl; }virtual void Func4() { cout << "Derive::Func4()" << endl; }
private:int _d = 2;
};
我们调试来观察一下虚表中有哪些变化

①基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法
②另外Func2继承下来后是虚函数,所以放进了虚表,Func2没有重写,所以派生类的虚表中依旧是Base::Func2,Func3也继承下来了,但是不是虚函数,所以不会放进虚表
③总结一下派生类的虚表生成:
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后
为什么Derive自己的虚函数Func4没有在虚函数表指针中看见?

通过调试观察内存窗口,我们可以看到_vfptr地址下肯定是存放了Derive::Func1和Base::Func2的地址的,它下面还有一个地址00b014c9,我们不确定这个地址是不是Func4的地址,我们现在来验证一下我们的猜想
//函数指针
typedef void (*VFPTR) ();
//依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数
void PrintVFT(VFPTR* vft)
{for (size_t i = 0; i < 3; i++){printf("%p->", vft[i]);VFPTR pf = vft[i];(*pf)();//pf();调用函数}
}
int main()
{Base b;Derive d;//函数指针数组VFPTR p[4];VFPTR* ptr = (VFPTR*)(*(int*)(&d));PrintVFT(ptr);return 0;
}
PrintVFT函数的思想:

我们可以通过这个函数来打印出_vfptr中的存放的那个地址到底是不是Func4

事实上,第三个位置存放的就是Func4的地址,只是vs只显示前两个,咱也不知道为什么,你也可以认为这是vs下的一个bug
总结:单继承虚函数对象模型如下

补充:
同类型的对象的虚表是同一个虚表

5.2 多继承中的虚函数表
以下面代码为例来讲解:
class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1;
};
int main()
{Derive d;return 0;
}
在监视窗口下我们来看一下对象d的结构,我们发现他有两个虚函数表,两个直接父类各自都有一个虚函数表,那Derive 类自生的虚函数func3应该存放在那个虚函数表中呢?

我们还是和之前一样,把虚函数打印出来看看,func3到底存在了那个虚表中,我们发现打印第一个虚函数表容易,但是第二个不好打印
下面提供两种方法打印第二个虚表的內容:
方法1:
VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
(char*)&d – 先取出d整体的地址来,然后强转成char*类型,这样+N就跳过N个字节,再+sizeof(Base1),加上Base1大小的字节,就可以跳过Base1,到Base2的位置,后面的操作就和以前的一样
缺陷:
这个方法有个缺陷,就是万一跳过Base1之后,后面紧挨着的不是Base2的地址怎么办,说不定有别的变量在Base1后面挨着,我们的方法2,就可以完美避开这个缺陷
方法2:
Base2* ptr = &d;
VFPTR* vTableb2 = (VFPTR*)(*(int*)ptr);
Base2* ptr = &d – 这一步直接就是取出d整体的地址,然后赋值给Base2的指针ptr,这里就发生了切片,ptr直接就是指向了Base2处,然后再进行之前的这个操作(VFPTR*)((int)ptr)就🆗的很
看打印的结果我们可以知道,Derive自己的虚函数func3放在了第一个继承的父类虚函数表中

所以多继承对象虚函数模型大体都是下面这样

没有彩蛋有🥚
来个小题收收尾
class A{
public:A(char *s) { cout<<s<<endl; }~A(){}
};
class B:virtual public A
{
public:B(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class C:virtual public A
{
public:C(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class D:public B,public C
{
public:D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3),A(s1){ cout<<s4<<endl;}
};
int main() {D *p=new D("class A","class B","class C","class D");delete p;return 0;
}

这题我们首先就可以排除B,C,对于派生类对象来说,在构造时,遵循的原则是先父后子,所以不可能最后初始化A,至于初始化class B和class C的顺序肯定是按照继承的顺序来,所以选A
多态暂且到这里,我后续会继续进行补充,谢谢大家的观看哦✋
相关文章:
【C++】多态:编程中的“一人千面”艺术
目录 一、多态的概念二、多态的定义及实现1.多态的构成条件2.虚函数的重写2.1 什么是虚函数?2.2 虚函数的重写是什么?2.3 虚函数重写的两个例外2.4 C11 override 和 final2.5 重载、覆盖(重写)、隐藏(重定义)的对比 三、抽象类3.1 概念3.2 接口继承和实现…...
【必备工具】gitee上传-保姆级教程
目录 1.gitee是什么 2.gitee怎么注册 编辑 3.gitee怎么提交代码 4.gitee的三板斧 Clone仓库 Q&A 1. Gitee 只有三板斧吗? 2. Git 教了,Gitee 上没有绿点怎么办? 3. 用户名和密码输入错误怎么办? 4. 操作时不小心…...
P1115 最长子段和
题目描述 给出一个长度为 𝑛n 的序列 𝑎a,选出其中连续且非空的一段使得这段和最大。 输入格式 第一行是一个整数,表示序列的长度 𝑛。 第二行有 𝑛n 个整数,第 𝑖 个整数表示序列的…...
02 FreeRTOS 任务
1、创建任务函数 1.1 动态内存的使用 在之前我们如果要创建一个与学生有关的任务,我们会定义: //打印50个学生的信息 char name[50][100]; int age[50]; int sex[50]; //1表示男,0表示女 int score[50]; 如果之后要对其进行修改会非常麻烦&…...
NSS题目练习4
[LitCTF 2023]1zjs 打开后是一个游戏,用dirsearch扫描,什么都没发现 查看源代码搜索flag,发现没有什么用 搜索php,访问 出现一堆符号,看样子像是jother编码 解码得到flag,要删掉[] [LitCTF 2023]Http pro …...
【算法】合并k个已排序的链表
✨题目链接: NC51 合并k个已排序的链表 ✨题目描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围:节点总数 0≤𝑛≤50000≤n≤5000,每个节点的val满足 ∣𝑣𝑎𝑙∣&…...
【Muduo】三大核心之EventLoop
Muduo网络库的EventLoop模块是网络编程框架中的核心组件,负责事件循环的驱动和管理。以下是对EventLoop模块的详细介绍: 作用与功能: EventLoop是网络服务器中负责循环的重要模块,它持续地监听、获取和处理各种事件,…...
ubuntu安装完桌面后如何启动
ubuntu安装完桌面后如何启动 在Ubuntu服务器上安装桌面环境后,您可以使用以下命令启动图形界面: sudo systemctl start gdm3如果您使用的是Ubuntu 20.04或更新版本,可能需要使用gdm3作为显示管理器。在早期的Ubuntu版本中,可能使…...
知识融合概述
文章目录 知识融合知识融合过程研究现状技术发展趋势 知识融合 知识融合的概念最早出现在1983年发表的文献中,并在20世纪九十年代得到研究者的广泛关注。而另一种知识融合的定义是指对来自多源的不同概念、上下文和不同表达等信息进行融合的过程认为知识融合的目标是…...
LIO-EKF: High Frequency LiDAR-Inertial Odometry using Extended Kalman Filters
一、论文摘要 里程计估计是每个需要在未知环境中导航的自主系统的关键要素。在现代移动机器人中,3D LiDAR 惯性系统通常用于执行此任务。通过融合 LiDAR 扫描和 IMU 测量,这些系统可以减少因顺序注册各个 LiDAR 扫描而引起的累积漂移,并提供稳…...
Shell脚本学习笔记(更新中...)
一、什么是shell shell的作用是: 解释执行用户输入的命令程序等。 用户输入一条命令,shell就解释一条。 键盘输入命令,LInux给与响应的方式,称之为交互式。 shell是一块包裹着系统核心的壳,处于操作系统的最外层&a…...
leetcode 210.课程表II
思路:拓补排序 其实就是对于第一个题的问题变了一个问法,上一个题本质上是求有没有环,这道题本质上就是让你求出来符合没有环的路径输出而已,本质上没有什么区别。 不同就在于这里需要你额外开一个数组用来存储你遍历这个有向图…...
SpringBootTest测试框架五
示例 package com.xxx;import com.xxx.ut.AbstractBasicTest; import com.xxx.ut.uttool.TestModel; import...
赛事|基于SprinBoot+vue的CSGO赛事管理系统(源码+数据库+文档)
CSGO赛事管理系统 目录 基于SprinBootvue的CSGO赛事管理系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3参赛战队功能模块 4合作方功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…...
线性化技巧:绝对值变量的线性化
文章目录 1. 问题2. 线性化3. 缺少 x i x i − 0 x_i^ \times x_i^- 0 xixi−0 有什么问题4. 延伸思考5. 参考文献 1. 问题 以方述诚老师课件中的案例为例: m a x 3 x 1 − 2 x 2 − 4 ∣ x 3 ∣ s . t . − x 1 2 x 2 ≤ − 5 3 x 2 − x 3 ≥ 6 2 x 1 …...
List基本使用(C++)
目录 1.list的介绍 2.list的使用 list的构造 list的size() 和 max_size() list遍历操作 list元素修改操作 assign()函数 push_front(),push_back 头插,尾插 pop_front() pop_back 头删尾删 insert()函数 swap()函数 resize()函数 clear()函数 list类数…...
ELK 日志监控平台(一)- 快速搭建
文章目录 ELK 日志监控平台(一)- 快速搭建1.ELK 简介2.Elasticsearch安装部署3.Logstash安装部署4.Kibana安装部署5.日志收集DEMO5.1.创建SpringBoot应用依赖导入日志配置文件 logback.xml启动类目录结构启动项目 5.2.创建Logstash配置文件5.3.重新启动L…...
工作中写单片机代码,与学校里有什么不同?
来聊聊我的经历,提供几个提升方向,亲测有效,希望能让你少走几年弯路。 10几年前,还没参加工作的时候,主要是玩玩开发板,也接触不到实际产品的代码,很好奇那些产品级的代码是怎样的。 第一份工作…...
Unity LayerMask避坑笔记
今天使用Physics2D.OverlapAreaNonAlloc进行物理检测时候,通过LayerMask.NameToLayer传入了int值的LayerMask,结果一直识别不到,经过Debug才找到问题,竟是LayerMask的“值”传输有问题,记录一下。 直接贴代码输出结果&…...
(原创)从右到左排列RecycleView的数据
问题的提出 当我们写一个Recycleview时,默认的效果大概是这样的: 当然,我们也可以用表格布局管理器GridLayoutManager做成这样: 可以看到,默认的绘制方向是: 从左到右,从上到下 那么问题来了…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

