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

C++之多态

文章目录

一、多态的概念

多态的定义与类型

二、多态的实现

三、虚函数

 虚函数的概念

虚函数的重写/覆盖

协变

析构函数的重写/覆盖

override,final关键字

override

final

纯虚函数与抽象类

三个概念辨析

四、多态实现的原理

虚函数表指针

动态绑定与静态绑定

虚函数表

虚函数表的作用

虚函数表的结构


前言

在上一节中,我们学习了C++中面向对象的三大特性之一的继承,我们通过继承更加灵活地使用代码,我们可以不用再重复写那些冗余的代码,对于一些成员相同或类似的类,我们可以通过继承来生成一个派生类。在这节,我们再来学习C++面向对象中三大特性的多态。


一、多态的概念

在学习内容之前,我们先来了解一下我们将要学习的东西是什么。我们上节通过字面意思简单地认识到继承就是从一个东西中获取一些东西过来。这里我们再来通过字面认识一下这个多态:多种行为形态。我们把它引入编程再理解一下:一个函数或方法在不同的情况下具有不同的行为。

多态是C++中面向对象编程的一个核心概念,允许同一个接口有多种实现方式,从而提高代码的灵活性和可扩展性。以下是多态的详细解释:

多态的定义与类型

多态(Polymorphism)指的是同一个函数或方法在不同的情况下表现出不同的行为。在C++中,多态分为两种类型:

  1. 编译时多态(静态多态):包括函数重载和运算符重载,编译器在编译阶段就确定调用哪个函数。
  2. 运行时多态(动态多态):通过虚函数和动态绑定实现,编译器在运行时根据实际对象类型调用相应的函数。

二、多态的实现

了解完了多态的概念,我们如何去实现这个多态呢?我们先从概念出发,多态是让一个函数或方法在不同的情况下表现出不同的行为,难道它和我们之前学习的函数重载有点关系吗?这里我提前给出答案——它与我们之前所学习的函数重载有点关系但是又有所区别。我们如果想要去实现多态必须满足以下两个重要条件

1.我们必须使用基类的指针或基类的引用去调用虚函数;

2.我们所调用的函数必须是虚函数,而且派生类的虚函数必须是经过重写/覆盖的。

(上面的两个条件我们仔细想想,其实是十分合理的:我们使用基类的指针或者基类的引用的话,我们才能既指向基类对象又能够指向派生类对象。至于虚函数我们在后面会着重介绍的,我们如果想要使一个函数具有多种行为,我们就需要在不同的派生类中对那个函数的函数体进行修改)

三、虚函数

 虚函数的概念

这里,我们再来学习一个新的概念——虚函数。它就相当于我们之前所学习的函数的一个子类,它具有函数的特性,另外又增加了一些特性。

我们将类成员函数前加上关键字virtual,那么这个成员函数就被称为虚函数。虚函数只能够是类成员函数,对于非成员函数是不可以的。(这里我再多嘴一句:这个virtual关键字,我们其实在上节的继承中就已经见过了,不过当时我们使用virtual是为了解决在多继承中菱形继承所带来的数据冗余和二义性的影响。我们注意不要和那个弄混了,这个就是一个关键字的不同用法)

class Animal
{
public:virtual void eat(){}protected:string _name;int _age;
};

虚函数的重写/覆盖

我们如果对于虚函数的修改叫做虚函数的重写/覆盖。我们不能够简单地在派生类中将函数体修改一下就行了。我们对于虚函数的重写/覆盖有一个强制要求:我们要确保在派生类中重写的虚函数必须函数名,函数返回值,函数参数列表与基类的虚函数保持一致。上面要求那三个相同,对于前两个是很容易看出来的,但是最后一个我们需要注意一下:有时候基类的虚函数参数给了一个缺省值,而派生类的虚函数的参数也给了一个缺省值,两个给定的缺省值不同,这时侯我们选择使用基类的缺省值(前提已经是多态关系了)

注意:我们写虚函数的时候,我们必须保证基类的虚函数中的virtual关键字写上这个是绝对不能缺的,派生类中的virtual关键字可写可不写,因为派生类是从基类中继承过来的,如果基类中已经是虚函数了,那么派生类中也是虚函数了,但是我们建议都带上virtual,这样别人看代码就一眼了之了。

协变

协变是虚函数重写中的一个特殊情况,我们在上面虚函数的重写/覆盖中我们已经提出要求——三同(函数名,函数返回值,函数参数列表),这里我们再来介绍一种特殊情况,区别于上面的那个要求,但是它也能够重写虚函数。派生类重写虚函数时,其函数返回值与基类的虚函数返回值不同,即基类的虚函数的函数返回值为基类对象指针或引用类型,派生类的虚函数的函数返回值为派生类对象指针或引用类型,称之为协变。

class A{};
class B:public A{};class Person
{
public:virtual A* func(){cout << "我是一个人" << endl;return nullptr;}
protected:string _name;int _age;
};class Student :public Person
{
public:virtual B* func(){cout << "我是一名学生" << endl;return nullptr;}
protected:int _id;string _address;
};void Fun(Person& ptr)
{ptr.func();
}int main()
{Person p;Student s;Fun(p);Fun(s);return 0;
}

从其运行结果,我们可以看出来这两个已经形成了多态关系,否则的话运行结果都是基类中的成员函数输出结果。

析构函数的重写/覆盖

对于析构函数的重写与普通的成员函数重写有点区别,在编译器编译时期,它们就将那些析构函数都转化为destructor()的形式,这样那些析构函数就都构成了隐藏关系,我们再在基类的析构函数上加上关键字virtual,那么与派生类的析构函数就构成了重写。

注意:我们对析构函数进行重写的话,我们一定要将基类的析构函数加上virtual,如果我们不加virtual的话,我们在进行释放资源的时候就会造成内存泄露了,我们不能够将派生类中的资源释放出去,反而会重复调用基类的析构函数,重复释放基类中的资源。

class A
{
public:virtual ~A(){cout << "~A()" << endl;}
};class B :public A
{
public:~B(){cout << "~B()->" <<_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;
}

如上代码:我们对基类中的析构函数加上了virtual关键字,于是派生类中的析构函数与基类的析构函数就构成了重写。于是我们在delete释放资源的时候,它们就能够正确释放资源(使用什么类对象的指针或用,我们就调用哪个类的析构函数,这样就避免了重复调用基类的析构函数这一情况)

override,final关键字

override

这两个关键字,是针对于虚函数重写的。有时候,其他的程序员写虚函数的时候,一些编程小白可能看不出来这是虚函数,于是我们就可以加上一个override关键字,然后编程小白在网上一查override这个关键字,于是知道了这个关键字是为了确保我们重写虚函数的正确性。

在C++编程语言中,override是一个关键字,用于明确指示编译器当前定义的函数是在重写(覆盖)基类中的一个虚函数。它是C++11标准引入的,旨在提高代码的可读性和可靠性。它通常放在函数的定义中,位于函数参数列表之后,返回类型之前。

主要作用

  1. 明确函数重写意图:通过使用override,开发者可以清楚地表明当前函数是在重写基类中的虚函数,而不是定义一个新的函数。
  2. 编译时检查:编译器在编译时会检查派生类中的函数与基类中的虚函数的签名(函数名称、参数类型、返回类型等)是否匹配。如果不匹配,编译器会报错,从而避免因函数签名不一致导致的错误。
  3. 提高代码可维护性:使用override使得代码更易读,其他开发者可以一目了然地看出哪些函数是在重写基类中的虚函数。

使用场景

override关键字主要用于以下场景:

  • 重写基类的虚函数:在派生类中定义函数时,使用override明确表示该函数是在重写基类中的虚函数。
  • 避免函数签名错误:通过编译时检查,确保派生类中的函数与基类中的虚函数签名一致,避免因拼写错误或参数类型不匹配导致的错误。

注意事项

  • 只能用于虚函数的重写override关键字只能在重写基类中的虚函数时使用。如果基类中的函数不是虚函数,使用override会导致编译错误。
  • 函数签名必须匹配:使用override时,派生类中的函数签名必须与基类中的虚函数完全一致,否则编译器会报错。
  • 提高代码质量:合理使用override可以提高代码的可靠性和可维护性,特别是在处理多态和虚函数时。

总结

override关键字是C++11引入的一个有用工具,它通过明确指示函数重写意图和进行编译时检查,帮助开发者编写更加可靠和高质量的代码。在处理多态和虚函数时,合理使用override可以有效避免因函数签名不匹配导致的错误,同时提高代码的可读性和可维护性

----------------------------------------------------------------------------------

final

至于final关键字,我们在之前继承中就已经见到了,我们当时使用这个关键字来修饰基类,那样我们就能够限制这个基类不能够被其他类继承。这里多态我们又见到了这个关键字,但是它修饰的对象不同,这次它修饰的是基类中虚函数,那样我们就能够限制其派生类不能够重写其虚函数。

在C++编程语言中,final是一个关键字,用于限制类的继承和虚函数的重写。它是C++11标准引入的,旨在提高代码的稳定性和可维护性。

主要作用

  1. 防止类被继承:在类定义中使用final关键字,可以防止该类被进一步继承。这意味着任何尝试从该类派生新类的操作都会导致编译错误。

  2. 防止虚函数被重写:在虚函数的定义中使用final关键字,可以防止该函数在派生类中被重写。这意味着任何派生类尝试重写该函数都会导致编译错误。

使用场景

final关键字主要适用于以下场景:

  • 防止意外继承:当设计一个类时,如果希望该类不能被继承,以确保类的结构和行为不会被意外修改,可以使用final关键字。

  • 防止虚函数被重写:在某些情况下,希望确保某个虚函数在派生类中不再被重写,以保持特定的行为不变,可以使用final关键字。

注意事项

  • 仅适用于C++11及以上版本final关键字是C++11引入的,因此需要确保编译器支持C++11标准。

  • override关键字结合使用:在派生类中重写基类的虚函数时,可以同时使用overridefinal关键字,以明确表示该函数是在重写基类的虚函数,并且不允许进一步重写。

  • 合理使用:虽然final关键字可以提高代码的稳定性,但过度使用可能会限制代码的灵活性。因此,应根据具体需求合理使用final关键字。

总结

final关键字是C++11引入的一个有用工具,它通过防止类被继承和虚函数被重写,帮助开发者编写更加稳定和可维护的代码。在需要确保类结构和行为不变的情况下,合理使用final关键字可以有效防止意外的继承或重写,从而提高代码的质量和可靠性。

纯虚函数与抽象类

对于虚函数转化为纯虚函数,只要在虚函数的参数列表括号后面加上=0即可。对于纯虚函数,我们只要声明一下就行了,可以不用定义实现(因为,纯虚函数本身咱们写出来就是为了让类变为抽象类。抽象类我们从它的名字上可以看出来,这个类并不是一个具体的类,是一种抽象的类,因此我们不能够使用抽象类进行实例化对象。

那么纯虚函数的具体作用到底是什么呢?主要是如下三个方面:

  1. 强制实现接口:纯虚函数(使用= 0声明)确保所有派生类必须提供该函数的具体实现,否则派生类将保持抽象状态,无法实例化。这在设计类层次结构时非常有用,确保每个派生类都遵循相同的接口。

  2. 实现多态:纯虚函数是实现运行时多态的核心。通过基类指针或引用调用纯虚函数时,会根据实际对象的类型调用相应的派生类实现,从而实现多态行为。

  3. 设计抽象接口:纯虚函数允许创建抽象基类,这些类定义了一组必须实现的方法,但不提供具体实现。这在设计灵活且可扩展的系统时非常有用,例如在处理不同文件格式或图形形状时,确保每个派生类都实现必要的方法。


class Car
{
public:virtual void Drive() = 0; //在虚函数后面加上=0,就变成了纯虚函数,//纯虚函数一般只要一个声明即可,可以不用定义,因为实际并没有什么作用//有纯虚函数的类是抽象类,抽象类是不可以进行实例化对象的};class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒适~" << endl;}
};class Bwm :public Car
{
public: virtual void Drive(){cout << "Bwm-快速~" << endl;}
};int main()
{//Car car;  //我们是不可以使用抽象类进行实例化对象的,但是我们可以使用抽象类作为类型Car* pBenz = new Benz;pBenz->Drive();Car* PBwm = new Bwm;PBwm->Drive();return 0;
}

如上代码,我们实现了一个纯虚函数,那么基类就是一个抽象类了,我们在main函数中试图想使用基类来实例化对象的,但是报错了。但是我们可以使用抽象类来作为类型(多态中的类型要是基类的指针或引用类型这里仍然是成立的)。这样我们通过纯虚函数这个小细节的增加,就很好地保证了多态的实现,以及基类中对虚函数重写。

三个概念辨析

如下图中的三个概念,初学者容易弄混,这里我单独拿出来介绍一下,它们都是在函数层次的不同概念,但是作用域要求以及对于函数的要求都有所不同,我们要清楚。

四、多态实现的原理

上面介绍了这么多概念,又是如果实现多态,又是多态的相关关键字,净是一些浮在表面的东西,我们要知道如何去用,我们也要通过现象看本质,我们要知道它们的背后的原理。

虚函数表指针

首先我要介绍的就是虚函数表指针,这个东西其实在上面我们已经使用过了,不过我们当时看不到而已。我们的多态的功能是让一个函数或方法在不同的情况下使用不同的行为,那么它们是如何去实现的呢?难道是编译器自动识别的嘛?NONONO,编译器可没你想的那么智能,其实在我们实现多态后,在基类和派生类中就出现了一个虚函数表,这个我们可以通过监视窗口看到有一个指针_vfptr,这个指针的全称是(virtual function table pointer)。这个指针指向的就是我们的虚函数表。虚函数表中放了我们基类和派生类的虚函数的地址,当我们在main函数中调用传递相应的参数时,这个指针就会去虚函数表中去查找相应的虚函数地址,然后进行相应的函数行为。

对于虚函数指针的大小是根据平台所决定的,不同的平台,指针大小不一样。如下图,这是在Vs2022中DebugX86环境下,虚函数指针的大小就是4,但是在X64环境下,它的大小就是8.

动态绑定与静态绑定

对不满足多态条件(指针或者引用+调用虚函数)的函数调用是在编译时绑定,也就是编译时确定调用函数的地址,叫做静态绑定

满足多态条件的函数调用是在运行时绑定,也就是在运行时到指向对象的虚函数表中找到调用函数 的地址,也就做动态绑定

虚函数表

在C++中,虚函数表(Virtual Table,简称vtable) 是实现动态绑定(运行时多态)的核心机制。它是编译器自动生成的一个数据结构,用于存储指向虚函数的指针。通过虚函数表,C++能够在运行时确定调用哪个具体的虚函数实现。


虚函数表的作用

  1. 实现动态绑定

    • 当通过基类指针或引用调用虚函数时,编译器无法在编译时确定具体调用哪个函数(因为具体实现可能在派生类中)。
    • 虚函数表允许在运行时根据实际对象的类型动态选择正确的函数实现。
  2. 支持多态

    • 虚函数表是实现多态性的关键。通过虚函数表,不同派生类可以提供不同的函数实现,而基类指针或引用可以透明地调用这些实现。
  3. 存储虚函数指针

    • 虚函数表中存储了所有虚函数的地址,每个类都有自己的虚函数表。
    • 当一个类继承自另一个类时,它的虚函数表会继承父类的虚函数表,并添加新的虚函数或覆盖已有的虚函数。

虚函数表的结构

  1. 虚函数指针(vptr)

    • 每个包含虚函数的类实例中都会有一个指向虚函数表的指针(通常称为 vptr)。
    • vptr 是编译器自动生成的,存储在对象的内存中。
  2. 虚函数表(vtable)

    • 虚函数表是一个数组,其中每个元素是一个函数指针,指向具体的虚函数实现。
    • 每个类都有自己的虚函数表,如果一个类没有虚函数,则不会生成虚函数表。

另外还有几处容易弄错的点

1.基类对象的虚函数表中存放的是基类所有虚函数的地址。同类型的对象共有一张虚函数表,不同类型的对象有各自独立的虚表,所以基类与派生类有各自独立的虚表;

2.派生类有两部分所组成,继承下来的基类和自己的成员,一般情况下,继承下来的基类中有虚函数表指针,自己就不会再生成虚函数表指针。但是要注意的这里继承下来的基类部分虚函数表指针和基类对象中的虚函数表指针并不是同一个,就行基类对象的成员和派生类对象中的基类对象成员不是同一个;

3.派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址;

4.派生类的虚函数表中包含:(1)基类的虚函数地址;(2)派生类重写的虚函数地址完成覆盖;(3)派生类自己的虚函数地址;

5.虚函数表存在哪里呢?这个问题C++标志中并没有明确规定,但是我们可以通过代码可以得知,VS下虚函数表是存在代码区(常量区)。

相关文章:

C++之多态

文章目录 一、多态的概念 多态的定义与类型 二、多态的实现 三、虚函数 虚函数的概念 虚函数的重写/覆盖 协变 析构函数的重写/覆盖 override,final关键字 override final 纯虚函数与抽象类 三个概念辨析 四、多态实现的原理 虚函数表指针 动态绑定与静态绑定 …...

从PDF中提取表格:以GB/T2260—2007为例

文章目录 先说结论前因后果思路1、PDF2CSV2、PDF2MD → MD2CSV3、针对不同表格的两种思路1&#xff09; 竖形三线表2&#xff09;五元素为一组 还没结束批量处理1、分割markdown文档2、跳过另一种格式的文档 总结一下 先说结论 结论就是&#xff0c;博主用了一天的时间去研究如…...

日常记录-群晖nas的docker注册表被墙,用Mac电脑的docker拉取镜像并安装到nas中

文章目录 前言一、拉取镜像二、安装到nas中总结 前言 群晖nas的docker注册表被墙&#xff0c;用Mac电脑的docker拉取镜像并安装到nas中 一、拉取镜像 群晖nas的架构师x86&#xff0c;Mac电脑的架构师arm。 在mac电脑中执行命令&#xff1a; # 镜像拉取 docker pull --platf…...

DeepSeek:重构办公效率的AI新范式

目录 一、效率跃迁的三重引擎 二、效率提升的量级突破 三、智能办公的范式转移 四、未来办公的效率奇点 当企业主面对堆积如山的文件审批、跨时区协作的沟通损耗、重复机械的数据整理时&#xff0c;是否想过这些场景正在吞噬团队的生产力&#xff1f;据麦肯锡研究显示&…...

AI小程序+SpringAI+管理后台+源码+支持动态添加大模型+支持动态添加AI应用

前言 今天给大家介绍一款 前端由uniapp开发的小程序&#xff0c;完美在小程序上运行&#xff0c;对话采用流式对话。后端由springbootspringai开发的应用软件源码。 功能简介 支持在管理后台动态新增“DeepSeek”&#xff0c;“openai”&#xff0c;“千帆”&#xff0c;“智…...

RAG的实现快速示例

RAG(Retrieval-Augmented Generation)其实就是结合了检索与生成,核心流程分为 检索(Retrieval) 和 生成(Generation) 两大阶段,通过外部知识库增强生成式模型的准确性和可靠性。 流程其实也很简单,如下图: 关于RAG的基本概念的介绍,可以参考: RAG(检索增强生成)快…...

利用 PHP 爬虫获取京东商品详情 API 返回值说明及代码示例

在电商领域&#xff0c;京东作为国内知名的电商平台&#xff0c;提供了丰富的商品信息。通过调用京东商品详情 API&#xff0c;我们可以获取商品的详细信息&#xff0c;如商品标题、价格、图片、描述等。这些信息对于数据分析、价格监控、商品推荐等场景具有重要价值。本文将详…...

PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制

在深度学习工程实践中&#xff0c;当训练大型模型或处理大规模数据集时&#xff0c;上述错误信息对许多开发者而言已不陌生。这是众所周知的 CUDA out of memory错误——当GPU尝试为张量分配空间而内存不足时发生。这种情况尤为令人沮丧&#xff0c;特别是在已投入大量时间优化…...

大模型基础知识扫盲

1 模型量化&#xff1a; 是什么&#xff1a;大模型量化是一种“压缩”技术&#xff0c;把模型里高精度的数字&#xff08;比如32位浮点数&#xff09;简化成低精度的数字&#xff08;比如8位定点数&#xff09;。 有什么用&#xff1a;它让模型占的空间更小&#xff0c;跑起来…...

《穿透表象,洞察分布式软总线“无形”之奥秘》

分布式系统已成为众多领域的关键支撑技术&#xff0c;而分布式软总线作为实现设备高效互联的核心技术&#xff0c;正逐渐走入大众视野。它常被描述为一条“无形”的总线&#xff0c;这一独特属性不仅是理解其技术内涵的关键&#xff0c;更是把握其在未来智能世界中重要作用的切…...

Python Cookbook-5.13 寻找子序列

任务 需要在某大序列中查找子序列。 解决方案 如果序列是字符串(普通的或者Unicode)&#xff0c;Python 的字符串的 find 方法以及标准库的re模块是最好的工具。否则&#xff0c;应该使用Knuth-Morris-Pratt算法(KMP): def KnuthMorrisPratt(text,pattern): 在序列text中找…...

(自用)蓝桥杯准备(需要写的基础)

要写的文件 led_app lcd_app key_app adc_app usart_app scheduler LHF_SYS一、外设引脚配置 1. 按键引脚 按键引脚配置如下&#xff1a; B1&#xff1a;PB0B2&#xff1a;PB1B3&#xff1a;PB2B4&#xff1a;PA0 2. LCD引脚 LCD引脚配置如下&#xff1a; GPIO_Pin_9 /* …...

STM32Cubemx-H7-14-Bootloader(上)-ST和串口烧录

前言 本文主要研究&#xff0c;如果把ST单片机的SWDIO和SWDCLK引脚改成推挽输出后&#xff0c;我们又应该怎么重新烧录&#xff0c;以及如何使用串口下载。 当没有设置STlink烧录为引脚或者设置成其他功能的时候 如果想恢复&#xff0c;那么就在烧录之前&#xff0c;一直按住…...

“深入浅出:Java中的Lambda表达式及其应用“

前言 Lambda表达式是Java 8引入的一项强大特性&#xff0c;它允许以更加简洁的方式表示匿名函数。Lambda表达式不仅让代码更加简洁、清晰&#xff0c;而且为函数式编程提供了有力支持&#xff0c;从而提升了Java语言的表达能力。 在本文中&#xff0c;我们将深入浅出地探讨La…...

6.1es新特性解构赋值

解构赋值是 ES6&#xff08;ECMAScript 2015&#xff09;引入的语法&#xff0c;通过模式匹配从数组或对象中提取值并赋值给变量。&#xff1a; 功能实现 数组解构&#xff1a;按位置匹配值&#xff0c;如 let [a, b] [1, 2]。对象解构&#xff1a;按属性名匹配值&#xff0c;…...

【从0到1学RabbitMQ】RabbitMQ高级篇

学完基础篇之后我们对用户下单这个业务进行了改造&#xff0c;我们可以吧用户支付这个业务抽出来&#xff0c;放入队列当中去执行。如下图&#xff1a; 但是这里我们思考一下&#xff0c;如果MQ通知失败了&#xff0c;支付服务中支付流水显示支付成功&#xff0c;而交易服务中…...

200 smart pid

PID整定控制面板-S7-200 SMART 跟我学/跟我做之PID功能-系列课程-西门子1847工业学习平台官网 使用西门子200SMART进行PID调节 PID自整定 PID调节技巧_哔哩哔哩_bilibili S7-200 SMART PID PID常见问题...

AI制作PPT,如何轻松打造高效演示文稿

AI制作PPT&#xff0c;如何轻松打造高效演示文稿&#xff01;随着信息化时代的到来&#xff0c;PPT已经成为了几乎所有职场人士、学生、讲师的必备工具。每个人都希望自己的PPT既有创意&#xff0c;又能高效展示信息。而在如今的科技背景下&#xff0c;AI的出现彻底改变了PPT的…...

如何用postman做接口自动化测试?

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 本文适合已经掌握 Postman 基本用法的读者&#xff0c;即对接口相关概念有一定了解、已经会使用 Postman 进行模拟请求等基本操作。 工作环境与版本&#xff1…...

day29-贪心__134. 加油站__135. 分发糖果__860.柠檬水找零__406.根据身高重建队列

134. 加油站 这道题的贪心方法相当的巧妙。 首先&#xff0c;我们可以通过gas[i] - cost[i]得到第i个站点的净加油量(耗油量)&#xff0c;那么如果我们现在考虑一个从某点a到某点b&#xff0c;那么如果a-》b范围之间的gas[i] - cost[i]存在负数&#xff0c;那么说明无法从a作…...

聊透多线程编程-线程基础-4.C# Thread 子线程执行完成后通知主线程执行特定动作

在多线程编程中&#xff0c;线程之间的同步和通信是一个常见的需求。例如&#xff0c;我们可能需要一个子线程完成某些任务后通知主线程&#xff0c;并由主线程执行特定的动作。本文将基于一个示例程序&#xff0c;详细讲解如何使用 AutoResetEvent 来实现这种场景。 示例代码…...

C# 组件的使用方法

类 Stopwatch 计算时间 Stopwatch sw new Stopwatch(); sw.Start(); // 要执行的代码块 Thread.Sleep(2000);sw.ElapsedMilliseconds // 消耗时间 Console.WriteLine(sw.ElapsedMilliseconds);组件 ListView 属性设置 外观 - View - Details 行为 - Columns -&#xff08;…...

Python常用排序算法

1. 冒泡排序 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;比较相邻的元素&#xff0c;如果他们的顺序错误就交换他们。 def bubble_sort(arr):# 遍历所有数组元素for i in range(len(arr)):# 最后i个元素是已经排序好的for j in range(0, …...

HTML5 服务器发送事件(Server-Sent Events)

1. 引言 HTML5 服务器发送事件&#xff08;Server-Sent Events&#xff0c;SSE&#xff09;是一种基于 HTTP 的服务器推送技术&#xff0c;允许服务器主动向客户端&#xff08;如浏览器&#xff09;发送实时更新。SSE 适用于单向通信场景&#xff0c;如新闻推送、实时价格更新…...

【C++游戏引擎开发】第12篇:GLSL语法与基础渲染——从管线结构到动态着色器

一、OpenGL渲染管线解密 1.1 OpenGL渲染管线流程图 #mermaid-svg-GrAgLUat95CVZKm0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GrAgLUat95CVZKm0 .error-icon{fill:#552222;}#mermaid-svg-GrAgLUat95CVZKm0 .e…...

阿里云负载均衡可以抗ddos吗

本文深度解析阿里云负载均衡的DDoS防护机制&#xff0c;通过实测数据验证其基础防御能力边界&#xff0c;揭示需结合云盾高防IP实现TB级流量清洗的工程实践。结合2023年Memcached反射攻击事件&#xff0c;提供混合云架构下的多层级防御方案设计指南。 云原生负载均衡的基础防护…...

动手学习:路径规划原理及常用算法

一、路径规划的基本原理 路径规划&#xff08;Path Planning&#xff09;是机器人导航的核心任务&#xff0c;目标是为机器人找到一条从起点到终点的无碰撞路径&#xff0c;同时满足约束条件&#xff08;如最短路径、最优能耗、安全性等&#xff09;。在人形机器人场景中&…...

Web前端性能指标Web3D性能优化

性能指标&评估方式 在Web3D性能优化之前,先了解性能指标&评估方式 前端性能指标评估与监测工具可分为以下几类,结合不同场景和需求,开发者可选择适合的工具进行性能优化: 一、浏览器内置工具 Chrome DevTools Performance 面板:记录运行时性能,分析CPU、内存使…...

Mujoco xml <option>

xml option option总起例子timestep(一般会用到)gravity(一般会用到)windmagneticdensityviscosityo_margino_solref, o_solimpo_frictionintegrator(一般会用到)cone(一般会用到)jacobian(一般会用到)solver(一般会用到)iterations(一般会用到)tolerance(一般会用到)noslip_it…...

如何用 nvm alias default 18.20.8 实现全局 Node.js 版本管理?一篇保姆级指南!!!

&#x1f4dd; 如何用 nvm alias default 18.20.8 实现全局 Node.js 版本管理&#xff1f;一篇保姆级指南 &#x1f680; 1. 核心命令解析 &#x1f50d; nvm alias default 18.20.8 是 nvm 管理工具中用于设置全局默认 Node.js 版本的核心命令。它的作用是将指定版本锁定为所…...

推荐一款Nginx图形化管理工具: NginxWebUI

Nginx Web UI是一款专为Nginx设计的图形化管理工具&#xff0c;旨在简化Nginx的配置与管理过程&#xff0c;提高开发者和系统管理的工作效率。项目地址&#xff1a;https://github.com/cym1102/nginxWebUI 。 一、Nginx WebUI的主要特点 简化配置&#xff1a;通过图形化的界…...

Pytest多环境切换实战:测试框架配置的最佳实践!

你是否也遇到过这种情况&#xff1a;本地测试通过&#xff0c;一到测试环境就翻车&#xff1f;环境变量错乱、接口地址混乱、数据源配置丢失……这些「环境切换」问题简直像定时炸弹&#xff0c;随时引爆你的测试流程&#xff01; 测试人员每天都跟不同的环境打交道&#xff0…...

大模型在网络安全领域的七大应用

1. 高级威胁检测与防御自动化 技术路径&#xff1a; 数据整合&#xff1a;聚合网络流量、终端日志、威胁情报等多源数据&#xff0c;构建多维特征库。行为建模&#xff1a;通过大模型的上下文理解能力&#xff0c;建立正常行为基线&#xff0c;识别偏离模式。动态策略生成&am…...

SpringBoot项目部署之启动脚本

一、启动脚本方案 1. 基础启动方式 1.1 直接运行JAR java -jar your-app.jar --spring.profiles.activeprod优点&#xff1a;简单直接&#xff0c;适合快速测试缺点&#xff1a;终端关闭即终止进程 1.2 后台运行 nohup java -jar your-app.jar > app.log 2>&1 &…...

【spark-submit】--提交任务

Spark-submit spark-submit 是 Apache Spark 提供的用于提交 Spark 应用程序到集群的命令行工具。 基本语法 spark-submit [options] <app-jar> [app-arguments]常用参数说明 应用程序配置 --class <class-name>: 指定应用程序的主类&#xff08;对于 Java/Sc…...

机器学习中的回归与分类模型:线性回归、逻辑回归与多分类

在机器学习领域&#xff0c;回归和分类是两类重要的任务&#xff0c;它们各自有着不同的应用场景和模型构建方式。本文将详细介绍线性回归、逻辑回归以及多分类任务的相关内容&#xff0c;包括数据预处理、模型定义、损失函数的选择以及评估指标的计算。 一、线性回归&#xf…...

spark-rdd

Spark-core RDD转换算子 RDD 根据数据处理方式的不同将算子整体上分为 Value 类型、双 Value 类型和 Key-Value 类型。 Value类型&#xff1a; 1.map 将处理的数据逐条进行映射转换&#xff0c;这里的转换可以是类型的转换&#xff0c;也可以是值的转换 mapPartitions map …...

Python 实现如何电商网站滚动翻页爬取

一、电商网站滚动翻页机制分析 电商网站如亚马逊和淘宝为了提升用户体验&#xff0c;通常采用滚动翻页加载数据的方式。当用户滚动页面到底部时&#xff0c;会触发新的数据加载&#xff0c;而不是一次性将所有数据展示在页面上。这种机制虽然对用户友好&#xff0c;但对爬虫来…...

pytorch TensorDataset与DataLoader类

读取数据 Dataset类 Dataset 是一个读取数据抽象类&#xff0c;所有自定义的数据集类需要继承该类。 该类主要实现以下三个功能 ①如何获取每一个数据及其label --> 抽象方法__getitem()__设置通过对象[索引]的方式获取每一个样本及其label ②告知一共有多少数据 -->…...

AI大模型与知识生态:重构认知的新时代引擎

📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:我们如何获得知识,正在被AI彻底改写 从古代图书馆、百科全书,到搜索引擎、问答社区,人类获取知识的方式一直在进化。而随着 ChatGPT、DeepSeek、Grok 等 AI 大模型的到来,这一过程迎来了颠覆…...

Server-Sent Events一种允许服务器向客户端发送实时更新的 Web API

Server-Sent Events&#xff08;SSE&#xff09;是一种允许服务器向客户端发送实时更新的 Web API。它基于 HTTP 协议&#xff0c;提供了一种单向的、服务器到客户端的通信机制&#xff0c;客户端可以通过监听服务器发送的事件来接收实时数据。下面从原理、使用场景、代码示例等…...

电子电器架构 --- AI如何重构汽车产业

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 周末洗了一个澡&#xff0c;换了一身衣服&#xff0c;出了门却不知道去哪儿&#xff0c;不知道去找谁&am…...

操作系统CPU调度

简介 当CPU有大量任务要处理,但由于资源有限,无法同时处理。所有就需要某种规则来决定任务处理的顺序,这就是调度。 调度层次 根据调度频率与层次,共分为三种 高级调度 也称为作业调度(Long-Trem Scheduling),频次很低,它决定哪些进程从外存(硬盘)加载到内存中级调度 也…...

icoding题解排序

数组合并 假设有 n 个长度为 k 的已排好序&#xff08;升序&#xff09;的数组&#xff0c;请设计数据结构和算法&#xff0c;将这 n 个数组合并到一个数组&#xff0c;且各元素按升序排列。即实现函数&#xff1a; void merge_arrays(const int* arr, int n, int k, int* out…...

xHCI 上 USB 读写分析

系列文章目录 xHCI 简单分析 USB Root Hub 分析 USB Hub 检测设备 usb host 驱动之 urb xHCI那些事儿 PCIe MMIO、DMA、TLP PCIe配置空间与CPU访问机制 PCIe总线协议基础实战 文章目录 系列文章目录一、xHCI 初始化二、xHCI 驱动识别根集线器&#xff08;RootHub&#xff09;…...

SpringCloud Alibaba 之分布式全局事务 Seata 原理分析

1. 什么是 Seata&#xff1f;为什么需要它&#xff1f; 想象一下&#xff0c;你去银行转账&#xff1a; 操作1&#xff1a;从你的账户扣款 1000 元操作2&#xff1a;向对方账户增加 1000 元 如果 操作1 成功&#xff0c;但 操作2 失败了&#xff0c;你的钱就凭空消失了&…...

《C语言中的“魔法盒子”:自定义函数的奇妙之旅》

&#x1f680;个人主页&#xff1a;BabyZZの秘密日记 &#x1f4d6;收入专栏&#xff1a;C语言 &#x1f30d;文章目入 一、引言二、自定义函数的创建&#xff08;一&#xff09;基本结构&#xff08;二&#xff09;一个简单的例子 三、自定义函数的使用&#xff08;一&#xf…...

【Spring】IoC 和 DI的关系、简单使用,从“硬编码“到“优雅解耦“:IoC与DI的Spring蜕变之旅

1.IoC 和 DI的关系 IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;和DI&#xff08;Dependency Injection&#xff0c;依赖注入&#xff09;是Spring框架中紧密相关但又有所区别的两个概念。理解它们的联系&#xff0c;可以帮助我们更深刻地掌握Spring…...

43、RESTful API 保姆教程

RESTful API 目录 RESTful API简介RESTful设计原则RESTful设计规范RESTful统一返回体JAX-RSJAX-RS与SpringBoot集成构建Restful服务实践总结一、RESTful API 简介 REST(Representational State Transfer)是一种基于HTTP的web服务架构风格,RESTful API则是遵循REST原则的网…...

ASP.NET Core 性能优化:客户端响应缓存

文章目录 前言一、什么是缓存二、客户端缓存核心机制&#xff1a;HTTP缓存头1&#xff09;使用[ResponseCache]属性&#xff08;推荐&#xff09;2&#xff09;预定义缓存配置&#xff08;CacheProfile&#xff09;3&#xff09;手动设置HTTP头4&#xff09;缓存验证机制&#…...