当前位置: 首页 > news >正文

【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::Func1Base::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 什么是虚函数&#xff1f;2.2 虚函数的重写是什么&#xff1f;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 只有三板斧吗&#xff1f; 2. Git 教了&#xff0c;Gitee 上没有绿点怎么办&#xff1f; 3. 用户名和密码输入错误怎么办&#xff1f; 4. 操作时不小心…...

P1115 最长子段和

题目描述 给出一个长度为 &#x1d45b;n 的序列 &#x1d44e;a&#xff0c;选出其中连续且非空的一段使得这段和最大。 输入格式 第一行是一个整数&#xff0c;表示序列的长度 &#x1d45b;。 第二行有 &#x1d45b;n 个整数&#xff0c;第 &#x1d456; 个整数表示序列的…...

02 FreeRTOS 任务

1、创建任务函数 1.1 动态内存的使用 在之前我们如果要创建一个与学生有关的任务&#xff0c;我们会定义&#xff1a; //打印50个学生的信息 char name[50][100]; int age[50]; int sex[50]; //1表示男&#xff0c;0表示女 int score[50]; 如果之后要对其进行修改会非常麻烦&…...

NSS题目练习4

[LitCTF 2023]1zjs 打开后是一个游戏&#xff0c;用dirsearch扫描&#xff0c;什么都没发现 查看源代码搜索flag&#xff0c;发现没有什么用 搜索php&#xff0c;访问 出现一堆符号&#xff0c;看样子像是jother编码 解码得到flag&#xff0c;要删掉[] [LitCTF 2023]Http pro …...

【算法】合并k个已排序的链表

✨题目链接&#xff1a; NC51 合并k个已排序的链表 ✨题目描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a;节点总数 0≤&#x1d45b;≤50000≤n≤5000&#xff0c;每个节点的val满足 ∣&#x1d463;&#x1d44e;&#x1d459;∣&…...

【Muduo】三大核心之EventLoop

Muduo网络库的EventLoop模块是网络编程框架中的核心组件&#xff0c;负责事件循环的驱动和管理。以下是对EventLoop模块的详细介绍&#xff1a; 作用与功能&#xff1a; EventLoop是网络服务器中负责循环的重要模块&#xff0c;它持续地监听、获取和处理各种事件&#xff0c;…...

ubuntu安装完桌面后如何启动

ubuntu安装完桌面后如何启动 在Ubuntu服务器上安装桌面环境后&#xff0c;您可以使用以下命令启动图形界面&#xff1a; sudo systemctl start gdm3如果您使用的是Ubuntu 20.04或更新版本&#xff0c;可能需要使用gdm3作为显示管理器。在早期的Ubuntu版本中&#xff0c;可能使…...

知识融合概述

文章目录 知识融合知识融合过程研究现状技术发展趋势 知识融合 知识融合的概念最早出现在1983年发表的文献中&#xff0c;并在20世纪九十年代得到研究者的广泛关注。而另一种知识融合的定义是指对来自多源的不同概念、上下文和不同表达等信息进行融合的过程认为知识融合的目标是…...

LIO-EKF: High Frequency LiDAR-Inertial Odometry using Extended Kalman Filters

一、论文摘要 里程计估计是每个需要在未知环境中导航的自主系统的关键要素。在现代移动机器人中&#xff0c;3D LiDAR 惯性系统通常用于执行此任务。通过融合 LiDAR 扫描和 IMU 测量&#xff0c;这些系统可以减少因顺序注册各个 LiDAR 扫描而引起的累积漂移&#xff0c;并提供稳…...

Shell脚本学习笔记(更新中...)

一、什么是shell shell的作用是&#xff1a; 解释执行用户输入的命令程序等。 用户输入一条命令&#xff0c;shell就解释一条。 键盘输入命令&#xff0c;LInux给与响应的方式&#xff0c;称之为交互式。 shell是一块包裹着系统核心的壳&#xff0c;处于操作系统的最外层&a…...

leetcode 210.课程表II

思路&#xff1a;拓补排序 其实就是对于第一个题的问题变了一个问法&#xff0c;上一个题本质上是求有没有环&#xff0c;这道题本质上就是让你求出来符合没有环的路径输出而已&#xff0c;本质上没有什么区别。 不同就在于这里需要你额外开一个数组用来存储你遍历这个有向图…...

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 xi​xi−​0 有什么问题4. 延伸思考5. 参考文献 1. 问题 以方述诚老师课件中的案例为例&#xff1a; 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 头插&#xff0c;尾插 pop_front() pop_back 头删尾删 insert()函数 swap()函数 resize()函数 clear()函数 list类数…...

ELK 日志监控平台(一)- 快速搭建

文章目录 ELK 日志监控平台&#xff08;一&#xff09;- 快速搭建1.ELK 简介2.Elasticsearch安装部署3.Logstash安装部署4.Kibana安装部署5.日志收集DEMO5.1.创建SpringBoot应用依赖导入日志配置文件 logback.xml启动类目录结构启动项目 5.2.创建Logstash配置文件5.3.重新启动L…...

工作中写单片机代码,与学校里有什么不同?

来聊聊我的经历&#xff0c;提供几个提升方向&#xff0c;亲测有效&#xff0c;希望能让你少走几年弯路。 10几年前&#xff0c;还没参加工作的时候&#xff0c;主要是玩玩开发板&#xff0c;也接触不到实际产品的代码&#xff0c;很好奇那些产品级的代码是怎样的。 第一份工作…...

Unity LayerMask避坑笔记

今天使用Physics2D.OverlapAreaNonAlloc进行物理检测时候&#xff0c;通过LayerMask.NameToLayer传入了int值的LayerMask&#xff0c;结果一直识别不到&#xff0c;经过Debug才找到问题&#xff0c;竟是LayerMask的“值”传输有问题&#xff0c;记录一下。 直接贴代码输出结果&…...

(原创)从右到左排列RecycleView的数据

问题的提出 当我们写一个Recycleview时&#xff0c;默认的效果大概是这样的&#xff1a; 当然&#xff0c;我们也可以用表格布局管理器GridLayoutManager做成这样&#xff1a; 可以看到&#xff0c;默认的绘制方向是&#xff1a; 从左到右&#xff0c;从上到下 那么问题来了…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...