【C++】多态 - 从虚函数到动态绑定的核心原理
📌 个人主页: 孙同学_
🔧 文章专栏:C++
💡 关注我,分享经验,助你少走弯路
文章目录
- 1. 多态的概念
- 2. 多态的定义及实现
- 2.1 多态的构成条件
- 2.1.1实现多态还有两个必须重要条件:
- 2.1.2 虚函数
- 2.1.3 虚函数的重写/覆盖
- 2.1.4 多态场景的一个选择题
- 2.1.5 虚函数重写的一些其他问题
- 2.1.6 override和final关键字
- 2.1.7 重载/重写/隐藏的对比
- 3. 纯虚函数和抽象类
- 4. 多态的原理
- 4.1 虚函数表指针
- 4.2 多态的原理
- 4.2.1 多态是如何实现的
- 4.2.2 动态绑定与静态绑定
- 4.2.3 虚函数表
1. 多态的概念
多态(polymorphism)的概念:通俗来说就是指多种形态,多态分为编译时多态(静态多态)和运行时多态(动态多态)。编译时多态(静态多态)主要是前面的函数重载和函数模板,它们传不同类型的参数就可以调用不同类型的函数,通过参数不同达到多种形态,之所以叫做编译时多态,是因为它们实参传给形参的参数匹配是在编译时完成的,我们把编译时一般归为静态,运行时归为动态。
运行时多态,具体点就是去完成某个行为(函数),传不同的对象就可以完成不同的行为,就达到多种形态。就如买票这个行为,当普通人买票时是全价买票,当学生买票时是学生票,军人买票时是优先买票。再比如,同样是动物叫的一个行为,传“猫”过去就是“喵喵”,传“狗”过去就是“汪汪”。
2. 多态的定义及实现
2.1 多态的构成条件
多态是一个继承关系下的类对象,去调用同一函数产生了不同行为。比如Student
继承了Person
。Person
对象买票全价,Student
对象优惠买票。
2.1.1实现多态还有两个必须重要条件:
- 必须是基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,并且完成了虚函数重写/覆盖。
说明:要实现多态的效果,第一必须是基类的指针或者引用,因为只有基类的指针或引用才能既指向基类的对象,又能指向派生类的对象。第二派生类必须对基类的虚函数完成重写/覆盖,重写/覆盖了基类和派生类之间才能有不同的函数,多态的不同形态效果才能达成。
2.1.2 虚函数
类成员函数前面加virtual
修饰,那么这个成员函数被称为虚函数。❗️注意:非成员函数不能加virtual
修饰。
class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};
2.1.3 虚函数的重写/覆盖
虚函数的重写/覆盖:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型,函数名字,参数列表完全相同),成派生类的虚函数重写了基类的虚函数。
注意:再重写基类虚函数时,派生类的虚函数在不加
virtual
关键字时,虽然也可以构成重写,因为继承后基类的虚函数被继承下来了,在派生类中依旧保持虚函数的属性),但是该种写法不是很规范,不建议这样使用。在考试题中会经常埋这样一个坑,让我们判断是否构成多态。
//指针
class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person
{
public:virtual void BuyTicket() {cout << "买票-打折" << endl;}
};void Func(Person* ptr)
{//这里虽然都是Person指针ptr调用BuyTicket//但是和ptr没关系,而是由ptr指向的对象决定的ptr->BuyTicket();
}int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}//引用class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person
{
public:virtual void BuyTicket() {cout << "买票-打折" << endl;}
};void Func(Person& ptr)
{//这里虽然都是Person指针ptr调用BuyTicket//但是和ptr没关系,而是由ptr指向的对象决定的ptr.BuyTicket();
}int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}
class Animal
{
public:virtual void talk() const{}
};
class Dog : public Animal
{
public:virtual void talk() const{std::cout << "汪汪" << std::endl;}
};
class Cat : public Animal
{
public:virtual void talk() const{std::cout << "(>^ω^<)喵" << std::endl;}
};
void letsHear(const Animal& animal)
{animal.talk();
}
int main()
{Cat cat;Dog dog;letsHear(cat);letsHear(dog);return 0;
}
2.1.4 多态场景的一个选择题
以下程序输出结果是什么()
A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确
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;
}
这里由this
调用func
,构成多态必须是父类的指针或者引用,这里是否构成多态呢?这里的this
是A*
,所以它是一个父类的指针。
继承它会把父类的成员拿下来是一种形象的说法,其实不会把它拿下来,它的复用是先去B
里面去找有没有test
,再去A
里面找,如果A
里面还找不到就会报错。复用的本质先去B
里面找,再去A
里面找。包括成员变量,如果A
里面有一个_a
,B
里面有一个_b
,它不是把A
里面的拷贝下来在B
里面也生成一份,是B
对象生成的时候会先生成一个A
对象的编译器,在这个对象模型在内存里面放的时候再放B
的成员,不会说是把分类的成员函数和成员对象都拷贝一份下来。
所以说第一个条件是满足的。
第二个条件是虚函数的重写,也就是func
,func
的函数名,参数类型,返回值都相同,它是父类的重写,所以也满足是虚函数。
所以是满足多态的,满足多态是指向谁调用谁,p
传给了this
,p
是指向一个派生类的B
对象的。
满足多态的情况下,调用子类重写的虚函数
2.1.5 虚函数重写的一些其他问题
- 协变(了解)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针或引用时,称为协变。
class A {};
class B : public A {};
class Person {
public:virtual A* BuyTicket(){cout << "买票-全价" << endl;return nullptr;}
};
class Student : public Person {
public:virtual B* BuyTicket(){cout << "买票-打折" << endl;return nullptr;}
};
void Func(Person* ptr)
{ptr->BuyTicket();
}
int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}
- 析构函数的重写
基类的析构函数为虚函数,此时派生类的析构函数只要有定义,无论是否加virtual
关键字都与基类函数构成重写,虽然基类与派生类析构函数的名字不同,看起来不符合重写规则,实际上编译器对派生类的析构函数的名称做了特殊处理,编译后析构函数的名称同一处理成destructor
,所以基类的析构函数加了virtual
修饰,派生类的析构函数就构成了重写。
class A
{
public:virtual ~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能
//构成多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0;
}
2.1.6 override和final关键字
C++对虚函数的重写要求比较严格,但是有些情况写由于疏忽,比如函数名写错或者是参数写错导致无法构成重写,而这种错误是在编译期间是不会报错的,只有在程序运行时没有得到预期结果,再进行找错误就会得不偿失,因此C++11
提供了override
,可以帮助用户检测是否重写。如果我们不想让派生类重写这个虚函数,那么就可以用final
去修饰。
// error C3668: “Benz::Drive”: 包含重写说明符“override”的方法没有重写任何基类方法
class Car {
public:virtual void Dirve(){}
};
class Benz :public Car {
public:virtual void Drive() override{ cout << "Benz-舒适" << endl;}
};int main()
{return 0;
}
// error C3248: “Car::Drive”: 声明为“final”的函数⽆法被“Benz::Drive”重写
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;}
};int main()
{return 0;
}
2.1.7 重载/重写/隐藏的对比
注意:这个概念对比经常考!!!
3. 纯虚函数和抽象类
在虚函数的后面加上=0
,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义,因为要被派生类重写,但语法上可以实现),只要声明即可。包含纯虚函数的类称为抽象类,抽象类不能实例化出对象,如果派生类继承后不重写虚函数,那么派生类也是抽象类。纯虚函数某种程度上强制了派生类必须重写虚函数,因为不重写实例化不出对象。
4. 多态的原理
4.1 虚函数表指针
下面编译为32位程序的运行结果是什么()
A. 编译报错 B. 运行报错 C. 8 D. 12
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;
}
4.2 多态的原理
4.2.1 多态是如何实现的
从底层的角度Func
函数中ptr->BuyTicket()
,是如何作为ptr
指向Person
对象调用Person::BuyTicket
,ptr
指向Student
对象调用Student::BuyTicket
的呢?通过下图我们可以看到满足多态条件后,底层不再是编译时,通过调用函数对象确定函数地址,而是运行时到指向对象的虚表中,确定对应的虚函数的地址。
第一张图,ptr
指向Person
对象,调用的是Person
的虚函数;第二张图ptr
指向的是Student
对象,调用的是Student
的虚函数。
多态:指向谁调用谁的虚函数
指向父类,运行时到指向父类对象的虚函数表中找到对应的虚函数进行调用
指向子类,运行时到指向子类对象切片出的父类的虚函数表中找到对应的虚函数进行调用。
4.2.2 动态绑定与静态绑定
- 对不满足多态条件(指针或者引用 + 调用虚函数)的函数调用是在编译时绑定,也就是编译时确定调用函数的地址,叫做静态绑定。
- 满足多态条件的函数调用是在运行时绑定,也就是在运行时到指向对象的虚函数表中,找到调用函数的地址,叫做动态绑定。
// ptr是指针+BuyTicket是虚函数满⾜多态条件。 // 这⾥就是动态绑定,编译在运⾏时到ptr指向对象的虚函数表中确定调⽤函数地址 ptr->BuyTicket();
00EF2001 mov eax,dword ptr [ptr]
00EF2004 mov edx,dword ptr [eax]
00EF2006 mov esi,esp
00EF2008 mov ecx,dword ptr [ptr]
00EF200B mov eax,dword ptr [edx]
00EF200D call eax// BuyTicket不是虚函数,不满⾜多态条件。 // 这⾥就是静态绑定,编译器直接确定调⽤函数地址 ptr->BuyTicket();
00EA2C91 mov ecx,dword ptr [ptr]
00EA2C94 call Student::Student (0EA153Ch)
4.2.3 虚函数表
- 基类对象的虚函数表中存放,同类型的对象公用同一张虚表,不同类型的对象各自有独立的虚表,所以基类和派生类各自有独立的虚表。
- 派生类由两部分构成,继承下来的基类和自己的成员,一般情况下,继承下来的基类中有虚函数表指针,自己就不再生成虚函数表指针。但是要注意的是这里继承下来的虚函数表指针和基类对象的虚函数表指针不是同一个,就像基类对象的成员,和派生类继承下来的基类对象的成员也是独立的。
- 派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址。
- 派生类的虚函数表中包括:(1)基类的虚函数地址(2)派生类重写的虚函数地址完成覆盖(3)派生类自己的虚函数地址
- 虚函数表本质上是一个存储虚函数指针的指针数组,一般情况下这个指针数组最后面放了一个
0x00000000
标记。(这个C++并没有进行规定,各个编译器自行定义的,vs系列编译器会再后⾯放个0x00000000标记,g++系列编译不会放) - 虚函数存在哪的?虚函数和普通函数一样,编译好是一段指令,都是存放在代码段的,只是虚函数的地址又存在虚表中。
- 虚函数表存放在哪的?这个问题严格说并没有标准答案C++标准并没有规定,我们写下面的代码可以对比验证⼀下。vs下是存在代码段(常量区)
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:// 重写基类的func1 virtual 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("Base虚表地址:%p\n", *(int*)p3);printf("Derive虚表地址:%p\n", *(int*)p4);printf("虚函数地址:%p\n", &Base::func1);printf("普通函数地址:%p\n", &Base::func5);return 0;
}
👍 如果对你有帮助,欢迎:
- 点赞 ⭐️
- 收藏 📌
- 关注 🔔
相关文章:
【C++】多态 - 从虚函数到动态绑定的核心原理
📌 个人主页: 孙同学_ 🔧 文章专栏:C 💡 关注我,分享经验,助你少走弯路 文章目录 1. 多态的概念2. 多态的定义及实现2.1 多态的构成条件2.1.1实现多态还有两个必须重要条件:2.1.2 虚…...
项目预期管理:超越甘特图,实现客户价值交付
引言 在项目管理实践中,许多项目经理习惯于将注意力集中在甘特图的进度条上,关注任务是否按时完成、里程碑是否达成。然而,这种以计划管理为中心的方法往往忽略了项目管理的核心目标:满足客户预期,交付真正的价值。项…...
FISCO 2.0 安装部署WeBASE与区块链浏览器(环境搭建)
FISCO BCOS 2.0 安装部署WeBASE与区块链浏览器-对应的官网地址: WeBASE平台:https://webasedoc.readthedocs.io/zh-cn/latest/docs/WeBASE/install.html 区块链浏览器:https://fisco-bcos-documentation.readthedocs.io/zh-cn/latest/docs/br…...
xss学习3之服务端session
一、服务端的Session 1. cookie和session 1)cookie和session对比 cookie: 保存在客户端,包含所有key-value信息,浏览器访问多个网站时会积累大量cookie,占用存储空间,并在每次请求时携带所有cookie,增加…...
23种设计模式-结构型模式之适配器模式(Java版本)
Java 适配器模式(Adapter Pattern)详解 🔌 什么是适配器模式? 适配器模式用于将一个类的接口转换成客户端所期望的另一种接口,让原本接口不兼容的类可以协同工作。 📦 就像插头转换器,让不同…...
【2025计算机网络-面试常问】http和https区别是什么,http的内容有哪些,https用的是对称加密还是非对称加密,流程是怎么样的
HTTP与HTTPS全面对比及HTTPS加密流程详解 一、HTTP与HTTPS核心区别 特性HTTPHTTPS协议基础明文传输HTTP SSL/TLS加密层默认端口80443加密方式无加密混合加密(非对称对称)证书要求不需要需要CA颁发的数字证书安全性易被窃听、篡改、冒充防窃听、防篡改…...
使用安全继电器的急停电路设计
使用安全继电器的急停电路设计 一,急停回路的设计1,如何将急停接到线路当中?2,急停开关 如何接到安全继电器中 一,急停回路的设计 急停是每一个设备必不可少的部分,因为关乎安全,所以说所以说他…...
SpringCloud概述和环境搭建
SpringCloud概述和环境搭建 一.微服务的引入1.单体架构2.集群和分布式架构3.集群和分布式4.微服务架构4.微服务的优缺点 二.微服务解决方案-SpringCloud1.Spring Cloud简介2.Spring Cloud版本3.Spring Cloud实现方案4.Spring Cloud Alibaba 三.环境搭建1.安装JDK172.Ubantu上下…...
System.out 详解
System.out 详解 System.out 是 Java 提供的标准输出流(PrintStream 类型),默认关联控制台(Console),用于向终端打印文本信息。它是 Java 中最常用的输出方式之一,尤其在调试和命令行程序开发中。 1. 核心知识点 (1)System.out 的本质 类型:PrintStream(字节流,但…...
每天学一个 Linux 命令(28):ln
可访问网站查看,视觉品味拉满: http://www.616vip.cn/28/index.html ln 是 Linux 中用于创建文件或目录链接的命令,主要生成硬链接(Hard Link)和符号链接(Symbolic Link,软链接)。链接常用于文件共享、快捷访问或版本管理。 命令格式 ln [选项] 源文件 目标链接链…...
【微知】服务器如何获取服务器的SN序列号信息?(dmidecode -t 1)
文章目录 背景命令dmidecode -t的数字代表的字段 背景 各种场景都需要获取服务器的SN(Serial Number),比如问题定位,文件命名,该部分信息在dmi中是标准信息,不同服务器,不同os都能用相同方式获…...
4.20刷题记录(单调栈)
第一部分:简单介绍 单调栈我的理解是在栈中存储数字出现的位置,然后通过遍历比较当前栈顶元素与当前元素的大小关系,从而确定逻辑相关顺序。 第二部分:真题讲解 (1)739. 每日温度 - 力扣(Lee…...
Opencv图像处理:模板匹配对象
文章目录 一、模板匹配1、什么是模板匹配?2、原理 二、单模板匹配(代码实现)1、预处理2、 开始模板匹配并绘制匹配位置的外接矩形 三、多模板匹配(代码实现)1、读取图片和模板2、模板匹配3、设置阈值1)阈值…...
Web3.0热门领域NFT项目实战课程
课程大小:3.8G 课程下载:https://download.csdn.net/download/m0_66047725/90616383 更多资源下载:关注我 深度掌握Solidity合约开发,助力成为抢手的Web3.0开发工程师 深入Web3.0技术的人才,一将难求。本课程由We…...
DAY 50 leetcode 1047--栈和队列.删除字符串中的所有相邻重复项
题号1047 给出由小写字母组成的字符串 s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 在 s 上反复执行重复项删除操作,直到无法继续删除。 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 import java.util.Ar…...
单例模式与消费者生产者模型,以及线程池的基本认识与模拟实现
前言 今天我们就来讲讲什么是单例模式与线程池的相关知识,这两个内容也是我们多线程中比较重要的内容。其次单例模式也是我们常见设计模式。 单例模式 那么什么是单例模式呢?上面说到的设计模式又是什么? 其实单例模式就是设计模式的一种。…...
微信小程序通过mqtt控制esp32
目录 1.注册巴法云 2.设备连接mqtt 3.微信小程序 备注 本文esp32用的是MicroPython固件,MQTT服务用的是巴法云。 本文参考巴法云官方教程:https://bemfa.blog.csdn.net/article/details/115282152 1.注册巴法云 注册登陆并新建一个topicÿ…...
QML、Qt Quick 、Qt Quick Controls 2
一、概念 基本关系 QML 是声明式语言,用于描述用户界面。声明式语法(类似JSON+JavaScript),定义UI结构和行为。 Qt Quick 是 QML 的标准库,提供基本类型和功能。提供QML语言运行时的基础能力,相当于QML的"标准模板库(STL)"。 Quick Controls 2 是基于 Qt Quic…...
基于maven-jar-plugin打造一款自动识别主类的maven打包插件
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…...
利用 HEMT 和 PHEMT 改善无线通信电路中的增益、速度和噪声
本文要点 高电子迁移率晶体管 (High electron mobility transistors ,HEMTs) 和应变式异质接面高迁移率晶体管(pseudomorphic high electron mobility transistors ,PHEMTs) 因其独特的、可提高性能的特点而…...
探秘C#用户定义类型:突破预定义的边界
在C#的编程世界里,除了系统提供的16种预定义类型,开发者还拥有强大的自主能力——创建自己的用户定义类型。这大大拓展了编程的灵活性和可扩展性,让开发者能根据具体需求定制数据结构和功能。 六种用户定义类型 类类型(class&am…...
idea中导入从GitHub上克隆下来的springboot项目解决找不到主类的问题
第一步:删除目录下的.idea和target,然后用idea打开 第二步:如果有需要,idea更换jdk版本 原文链接:https://blog.csdn.net/m0_74036731/article/details/146779040 解决方法(idea中解决)&#…...
北理工宫某的瓜ppt下载地址
关于“北理工宫某瓜”PPT下载地址相关技术探讨 摘要:本文围绕“北理工宫某瓜”事件中PPT下载地址相关情况展开分析,探讨了网络资源传播的技术机制、涉及的网络安全问题以及围绕此类资源分享应遵循的规范和注意事项,旨在从技术角度对这类网络…...
[论文阅读]Making Retrieval-Augmented Language Models Robust to Irrelevant Context
Making Retrieval-Augmented Language Models Robust to Irrelevant Context [2310.01558v2] Making Retrieval-Augmented Language Models Robust to Irrelevant Context 检索增强语言模型(RALMs),它包含一个检索机制,以减少将…...
论文阅读:2023 arxiv A Survey of Reinforcement Learning from Human Feedback
A Survey of Reinforcement Learning from Human Feedback https://arxiv.org/pdf/2312.14925 https://www.doubao.com/chat/3506943124865538 速览 这篇论文是关于“从人类反馈中进行强化学习(RLHF)”的综述,核心是讲如何让AI通过人类反…...
【图像处理基石】什么是去马赛克算法?
RAW数据的Demosaic算法(去马赛克算法)是图像处理中的关键技术,主要用于将图像传感器(如数码相机、手机摄像头)采集的原始马赛克数据恢复为完整的RGB三通道图像。 1. RAW数据的特性 马赛克结构:图像传感器…...
transformer注意力机制
单头注意力机制 import torch import torch.nn.functional as Fdef scaled_dot_product_attention(Q, K, V):# Q: (batch_size, seq_len, d_k)# K: (batch_size, seq_len, d_k)# V: (batch_size, seq_len, d_v)batch_size: 一次输入的句子数。 seq_len: 每个句子的词数。 d_mo…...
Ubuntu 22.04 更换 Nvidia 显卡后启动无法进入桌面问题的解决
原显卡为 R7 240, 更换为 3060Ti 后, 开机进桌面时卡在了黑屏界面, 键盘有反应, 但是无法进入 shell. 解决方案为 https://askubuntu.com/questions/1538108/cant-install-rtx-4060-ti-on-ubuntu-22-04-lts 启动后在开机菜单中(如果没有开机菜单, 需要按shift键), 进入recove…...
ROS机器人开发实践->机器人建模与仿真
前言: 这篇博客知识一个整体性的了解对于机器人建模和仿真,更多详细的细节,见 6.4.2 Xacro_语法详解 Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 一、整体框架 机器人模型分为两个部分具体的形状和插件。有了这个具体的形状…...
中国占全球工业机器人装机量的52%,国产机器人崛起加速洗牌,拆分机器人业务独立上市,软硬件协同增强,AI工业机械臂催生业务再增长
一、内部战略优化:聚焦核心业务与释放增长潜力 业务协同效应有限 ABB的机器人业务(全球市场份额第二)与集团其他业务(如电气化、过程自动化)的协同性较低。机器人业务专注于柔性制造和智能自动化,而其他业务更偏向能源效率和大型工业系统。分拆后,ABB集团可更聚焦于电气…...
C#森林中的兔子(力扣题目)
C#森林中的兔子(力扣题目) 题目介绍 森林中有未知数量的兔子。提问其中若干只兔子 “还有多少只兔子与你(指被提问的兔子)颜色相同?” ,将答案收集到一个整数数组 answers 中,其中 answers[i] 是第 i 只兔子的回答。 给你数组…...
OSPF特殊区域
四种特殊区域 1、stub 2、完全stub 3、nssa 4、完全nssa 作用:用于优化OSPF的LSDB空间 stub: [R2-ospf-1-area-0.0.0.1]stub //配置一个区域为stub区域 只在ABR上配置的话会导致OSPF邻居关系断开,因为此时Option选项中Nbit和Ebit置位不一致所…...
深入理解 CICD 与 Jenkins 流水线:从原理到实践
前言:在当今数字化飞速发展的时代,软件开发行业的竞争日益激烈。为了能够快速响应市场需求,及时交付高质量的软件产品,开发团队们不断探索和采用新的开发模式与工具。CICD(持续集成、持续交付 / 部署)作为一…...
1.Vue自动化工具安装(Vue-cli)
目录 1.node.js 安装: 2 npm 安装 3 安装Vue-cli 4总结: 一般情况下,单文件组件,我们运行在 自动化工具vue-CLI中,可以帮我们编译单文件组件。所以我们在学习时一般需要在系统中先搭建vue-CLI工具 下面就是一些我…...
前端亮点:大文件上传技术详解及问题解析
大片文件上传 文件上传 大片文件上传需考虑问题 一、核心实现步骤 分片唯一标识计算 (优化比较时间) • Hash生成:使用SparkMD5或crypto.subtle.digest计算文件整体Hash(秒传依据)及分片Hash(断点续传依据)。 • 优化:通过Web Worker多线程计算,避免主线程阻塞(如…...
每日一题——最小测试用例集覆盖问题
最小测试用例集覆盖问题(C语言实现) 问题描述 假设我们有一系列测试用例,每个测试用例会覆盖若干个代码模块。 我们使用一个二维数组来表示这些测试用例的覆盖情况: 如果某个测试用例 i 能覆盖代码模块 j,则数组中…...
React 文章 分页
删除功能 携带路由参数跳转到新的路由项 const navigate useNavigate() 根据文章ID条件渲染...
【技术派后端篇】Redis实现统计计数
在互联网项目中,计数器有着广泛的应用场景。以技术派项目为例,诸如文章点赞数、收藏数、评论数以及用户粉丝数等都离不开计数器的支持。在技术派源码中,提供了基于数据库操作记录实时更新和基于 Redis 的 incr 特性实现计数器这两种方案&…...
NHANES指标推荐:RFM
文章题目:Higher relative fat mass was associated with a higher prevalence of gallstones in US adults DOI:10.1186/s12876-025-03715-3 中文标题:在美国成年人中,相对脂肪质量越高,胆结石患病率就越高 发表杂志&…...
嵌入式人工智能应用-第三章 opencv操作 4 灰度处理
嵌入式人工智能应用 嵌入式人工智能应用-第三章 opencv操作 4 灰度处理 嵌入式人工智能应用1 灰度处理2 算法2.1 均值方法2.2 最大值法2.3 分量法2.4 加权平均法(Weighted Average Method)2.5 系统自带方法 3 总结 1 灰度处理 图像灰处理即是将一幅彩色…...
AI Agent破局:智能化与生态系统标准化的颠覆性融合!
Hi!好久不见 云边有个稻草人-个人主页 热门文章_云边有个稻草人的博客-本篇文章所属专栏~ 目录 一、引言 二、AI Agent的基本概念 2.1 定义与分类 2.2 AI Agent的工作原理 2.3 示例代码:AI Agent的基本实现 三、AI Agent在企业数字化转型中的应用 …...
UniFlash以串口方式烧录MSPM0G3507(无需仿真器)
材料:MSPM0G3507黑钢版,只要有UART的其他版本亦可(PA14需接LED) 下载软件:UniFlash 9.1.0.5175,网址:UNIFLASH 软件编程工具 | 德州仪器 TI.com.cn 测试文件:MSPM0G30…...
坐标轴刻度QCPAxisTicker
一、QCPAxisTicker 概述 QCPAxisTicker 是 QCustomPlot 中控制坐标轴刻度生成和显示的基类,负责计算刻度位置和生成刻度标签。 二、主要派生类 类名描述QCPAxisTickerFixed固定步长的刻度生成器QCPAxisTickerLog对数坐标刻度生成器QCPAxisTickerPi专门显示π倍数…...
Spring Boot 版本与对应 JDK 版本兼容性
Spring Boot 版本与对应 JDK 版本兼容性 以下是 Spring Boot 主要版本与所需 JDK 版本的对应关系,以及长期支持(LTS)信息: 最新版本对应关系 (截至2024年) Spring Boot 版本发布日期支持的 JDK 版本备注3.2.x (最新)2023-11JDK 17-21推荐使用 JDK 173…...
【MySQL】MySQL的基础语法及其语句的介绍
1、基础语法 mysql -h【主机名】 -u【用户名】 -p //登录MySQL exit或quit; //退出MySQL show database; //查看MySQL下的所有数据库 use 【数据库名】; //进入数据库 show tables; //查看数据库下的所有表名 *MySQL的启动和关闭 &am…...
《汽车理论》第四章作业MATLAB部分
1.计算并绘制利用附着系数曲线和制动效率曲线 clc close all %空载(no load)-1 ;满载(full load)-2 m14080; m29290; hg10.845; hg21.170; L3.950; a12.100; a22.950; b1L-a1; b2L-a2; beta0.38; %利用附着系数与制动强度的关系曲线 z0:0.01:1; phi_f1L*beta.*z./(b1z*hg1);%前…...
SpringCloud实战
环境准备: 1. 一台虚拟机,部署好centos7操作系统、安装好docker 2. 使用docker安装mysql数据库且启动mysql容器 3. IDEA配置的JDK版本是11 4. 前端代码启动Nginx 一、单体架构和微服务的区别? 1. 单体架构 将业务的所有功能集中在一个项目中…...
Cribl 对Windows-xml log 进行 -Serialize-05
The Serialize Function Description The Serialize Function is designed to transform an events content into a predefined format. Steps - Adding a Serialize Function important Select the Add Function<...
鸿蒙ArkUI之布局实战,线性布局(Column,Row)、弹性布局(Flex)、层叠布局(Stack),详细用法
本文聚焦于ArkUI的布局实战,三种十分重要的布局,线性布局、弹性布局、层叠布局,在实际开发过程中这几种布局方法都十分常见,下面直接上手 线性布局 垂直布局(Column) 官方文档: Column-行列…...
缓存 --- 内存缓存 or 分布式缓存
缓存 --- 内存缓存 or 分布式缓存 内存缓存(In-Memory Cache)分布式缓存(Distributed Cache)内存缓存 vs 分布式缓存 内存缓存和分布式缓存是两种常见的缓存策略,它们在存储位置、访问速度和适用场景上有所不同。下面分…...