【C++】多态
目录
文章目录
前言
一、多态的概念
二、多态的定义及实现
三、重载/重写/隐藏的对比
四、纯虚函数和抽象类
五、多态的原理
总结
前言
本文主要讲述C++中的多态,涉及的概念有虚函数、协变、纯虚函数、抽象类、虚表指针和虚函数表等。
一、多态的概念
多态分为:
- 编译时多态(静态多态):主要就是我们前面讲的函数重载和函数模板,他们传不同类型的参数就可以调用不同的函数,通过参数不同达到多种形态,
- 运行时多态(动态多态):运行时多态,具体点就是去完成某个行为(函数),可以传不同的对象就会完成不同的行为,就达到多种形态。比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是优惠买票(5折或75折);军人买票时是优先买票。
我们把编译时一般归为静态,运行时归为动态。
本文主要讲解的就是动态多态
二、多态的定义及实现
1.定义
- 首先是继承。
- 多态是一个继承关系的下的类对象,去调用同一函数,产生了不同的行为。比如Student继承了 Person。Person对象买票全价,Student对象优惠买票。
实现多态还有两个必须条件:
- 必须是基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,并且完成了虚函数重写/覆盖。
说明:要实现多态效果,第⼀必须是基类的指针或引用,因为只有基类的指针或引用才能既指向基类对象又指向派生类对象;第二派生类必须对基类的虚函数完成重写/覆盖,重写或者覆盖了,基类和派生类之间才能有不同的函数,多态的不同形态效果才能达到。
虚函数:
- 关键字:virtual
- 类成员函数前面加 virtual 修饰,那么这个成员函数被称为虚函数。注意非成员函数不能加 virtual 修饰。(包括静态成员函数也不能加virtual)
虚函数演示:
class Person
{
public:virtual void BuyTicket()//虚函数{cout << "买票-全价" << endl;}
};
虚函数的重写(覆盖):
- 派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派生类的虚函数重写了基类的虚函数。(只有函数体不相同)
- 注意:在重写基类虚函数时,派生类的虚函数在不加 virtual 关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用,不过在考试选择题中,经常会故意买这个坑,让你判断是否构成多态。(简单点说,派生类会直接把虚函数声明继承下来,重写时只改变函数体)
虚函数重写演示:
class Person
{
public:virtual void BuyTicket()//虚函数{cout << "买票-全价" << endl;}
};class Student : public Person//继承
{
public:virtual void BuyTicket()//虚函数重写{cout << "买票-打折" << endl;}
};
2.实现
定义多态演示1:买票
#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket()//虚函数{cout << "买票-全价" << endl;}
};class Student : public Person//继承
{
public:virtual void BuyTicket()//虚函数重写{cout << "买票-打折" << endl;}
};int main()
{Person p1;Student s1;Person* ptr1 = &p1;//必须是基类的指针或者引用ptr1->BuyTicket();//被调用的函数必须是虚函数Person& ptr2 = s1;//必须是基类的指针或者引用ptr2.BuyTicket();//被调用的函数必须是虚函数return 0;
}
运行结果:
演示2:动物叫
#include <iostream>
using namespace std;class Animal
{
public:virtual void talk() const//虚函数{}
};class Dog : public Animal//继承
{
public:virtual void talk() const//虚函数重写{cout << "汪汪" << endl;}
};class Cat : public Animal//继承
{
public:virtual void talk() const//虚函数重写{cout << "(>^ω^<)喵" << endl;}
};void LetsHear(const Animal& a)//基类指针或者引用调用
{a.talk();
}int main()
{Dog g;Cat c;LetsHear(g);LetsHear(c);return 0;
}
运行结果:
3.多态场景的一个选择题:
#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;
}
以上程序输出结果是什么?()
A:A->0 B:B->1 C:A->1 D:B->0 E:编译出错 F:以上都不正确
- 首先,初学时这道题很难选对。主函数中,派生类指针 p 调用 test 函数。
- test 函数是在父类中,这里需要注意,此时 test 的 this 指针是 A类 而不是 B类,我们虽然形象上说继承是把父类的成员拿到子类中,但其实不是真正的拿,而是当编译器调用父类成员时,如果在子类找不到就会去父类中找。
- 我们知道 test 函数中 this 指针是 A类 的类型时,test 中就使用父类指针调用 func 函数,此时构成多态,因为 func 是虚函数,并且在A类中完成了重写,这里需要注意的是A类中 func 函数虽然没有加 virtual,但还是重写,只要父类加了 virtual 就行。
- 多态构成条件:1.父类指针或引用调用虚函数(满足),2.虚函数完成重写(满足)。所以 test 中调用的 func 是B类中的 func。此时你可能以为正确答案是 D。
- 正确答案是:B,因为还有一个点需要注意,上文已经说过,虚函数重写只需要函数体不同即可,这是因为在子类中,是直接将父类的虚函数声明拿过来,所以在子类中缺省值其实是 1 而不是 0。
运行结果验证:
- 这题其实是一个警示,告诫我们重写虚函数时要保持缺省参数一致,并且子类虚函数最好也要带上 virtual 关键字。
4.协变
- 派生类重写基类虚函数时,与基类虚函数返回值类型可以不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。协变的实际意义并不大,所以我们了解一下即可。
- 简单点说:只要父类虚函数的返回值类型与子类虚函数返回值类型构成父子关系(继承关系),也构成虚函数重写。
- 也就是说,虚函数重写不一定非要返回值相同,因为有协变这种特殊情况。
协变演示:
#include <iostream>
using namespace std;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 p1;Student s1;Func(&p1);Func(&s1);return 0;
}
运行结果:
- 父类虚函数与子类虚函数返回值 A* 和 B* ,A类和B类构成父子关系(继承关系),这就是协变,构成虚函数重写,也就支持多态。
- 当然,返回值是当前父子类也是一样的。
演示:
#include <iostream>
using namespace std;class Person
{
public:virtual Person* BuyTicket()//虚函数{cout << "买票-全价" << endl;return nullptr;}
};class Student : public Person
{
public:virtual Student* BuyTicket()//协变{cout << "买票-打折" << endl;return nullptr;}
};void Func(Person* ptr)
{ptr->BuyTicket();
}int main()
{Person p1;Student s1;Func(&p1);Func(&s1);return 0;
}
运行结果:
总结,协变只要虚函数返回值构成父子关系即可。
5.析构函数的重写
- 基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加 virtual 关键字,都与基类的析构函数构成重写。
- 虽然基类与派生类析构函数名字不同看起来不符合重写的规则,实际上编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成 destructor,所以基类的析构函数加了 vialtual修饰,派生类的析构函数就构成重写。
为什么基类的析构函数建议设计成虚函数?
我们可以通过下面一个例子来说明:
#include <iostream>
using namespace std;class A
{
public:virtual ~A()//写成虚函数{cout << "~A()" << endl;}
};class B : public A
{
public:~B()//无论加不加virtual都构成虚函数重写{delete[] _p;cout << "~B()" << endl;}protected:int* _p = new int[10];
};int main()
{A* p1 = new A;A* p2 = new B;delete p1;//假如不构成多态,p2就调用不到B类的析构导致内存泄漏delete p2;return 0;
}
运行结果:
- 第一个析构是 p1 调用A类的析构,第二、三个析构是p2调用B类的析构,因为继承所以最后还会析构父类。
- 这里析构形成多态就不怕因为调用不到 B 类的析构而导致内存泄漏等问题了。
从反汇编处就能看到A、B类的析构函数名字都被处理成一样的了,所以满足虚函数重写条件之一的函数名相同:
6.override和final关键字
- 从上面可以看出,C++对虚函数重写的要求比较严格,但是有些情况下由于疏忽,比如函数名写错参数写错等导致无法构成重写,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来 debug 会得不偿失,因此C++11提供了override,可以帮助用户检测是否完成重写。
- 如果我们不想让派生类重写这个虚函数,那么可以用final去修饰。
override示例:
#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket()//虚函数{cout << "买票-全价" << endl;}
};class Student : public Person
{
public://加上override可以判断是否完成重写,比如这里故意写成函数名virtual void BuyTicke() override{cout << "买票-打折" << endl;}
};int main()
{Person p1;Student s1;Person* ptr1 = &p1;ptr1->BuyTicket();Person& ptr2 = s1;ptr2.BuyTicket();return 0;
}
报错信息:
final示例:
#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket() final//不能重写{cout << "买票-全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket() override{cout << "买票-打折" << endl;}
};int main()
{Person p1;Student s1;Person* ptr1 = &p1;ptr1->BuyTicket();Person& ptr2 = s1;ptr2.BuyTicket();return 0;
}
报错信息:
三、重载/重写/隐藏的对比
四、纯虚函数和抽象类
- 在虚函数的后面写上 =0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被派生类重写,但是语法上可以实现),只要声明即可。
- 包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象,如果派生类继承后不重写纯虚函数,那么派生类也是抽象类。
- 纯虚函数某种程度上强制了 派生类重写虚函数,因为不重写实例化不出对象。
演示:纯虚函数和抽象类
#include <iostream>
using namespace std;//抽象类
class Car
{
public:virtual void Drive() = 0;//纯虚函数
};class Benz : public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;}
};class Bmw : public Car
{
public:virtual void Drive(){cout << "Bwm-操控" << endl;}
};int main()
{Car* p1 = new Benz;p1->Drive();Car* p2 = new Bmw;p2->Drive();return 0;
}
运行结果:
- 简单说,抽象类就是专门用来继承的类,它可以表示某一个抽象概念,抽象的东西是不能实例化的。
- 比如上面抽象类 Car(车),因为不是什么具体的车所以不实例化,当其子类重写纯虚函数后就可以表示 Benz(奔驰) 或者 Bmw(宝马),这样就可以实例化具体的车了。
五、多态的原理
1.虚函数表指针
问:下面编译为32位程序的运行结果是什么()
A.编译报错 B.运行报错 C.8 D.12
#include <iostream>
using namespace std;class Base
{
public:virtual void func1(){cout << "func1()" << endl;}
protected:int _a = 1;char _ch = 'x';
};int main()
{Base b;cout << sizeof(b) << endl;//32位return 0;
}
- 首先,我们知道关于一个类对象的大小,只计算它的成员变量,函数是储存在静态区的
- 所以表面上我们只计算 _a 和 _ch 的大小,一个占4字节,一个占1字节,按照内存对齐结果应该是占 8 字节。但是...
- 实际结果是选 D,12字节,因为这里存在一个虚函数表指针,指针在32位下占4字节,按照内存对齐结果就是12字节。
运行结果:
我们可以通过监视窗口看到虚函数表指针:
- 这里多了一个变量 _vfptr 就是虚函数表指针,也可以叫虚表指针,它是一个指针数组,专门存储虚函数地址的。
- 那么C++实现多态的原理就是全靠这个 虚函数表 了。
- 注意:只有定义了虚函数或者继承了父类的虚函数,才有这个虚函数表指针变量。
2.多态原理
- 父类定义了虚函数,那么虚函数表是父类子类都有的,对于子类的虚函数表,如果重写了父类的虚函数,那么子类对应虚函数的地址就不一样,这样当父类指针调用子类或者父类时,就会根据虚函数表中不同的地址调用不同的函数,以此就形成了多态。
- 所谓动态多态:就是程序在运行时根据虚函数表中的函数地址调用不同函数来实现的。
3.动态绑定与静态绑定
- 对不满足多态条件(指针或者引用+调用虚函数)的函数调用是在编译时绑定,也就是编译时确定调用函数的地址,叫做静态绑定。
- 满足多态条件的函数调用是在运行时绑定,也就是在运行时到指向对象的虚函数表中找到调用函数的地址,也就做动态绑定。
反汇编可以看出区别:
动态绑定:先存在寄存器中,然后去寄存器中call对应函数地址
静态绑定:函数地址在编译时就直接确认了的
4.虚函数表
关于虚函数表还有一些需要补充的:
- 1.基类对象的虚函数表中存放基类所有虚函数的地址。同类型的对象共用同一张虚表,不同类型的对象各自有独立的虚表,所以基类和派生类有各自独立的虚表。
- 2.派生类由两部分构成,继承下来的基类和自己的成员,一般情况下,继承下来的基类中有虚函数表指针,自己就不会再生成虚函数表指针。但是要注意的这里继承下来的基类部分虚函数表指针和基类对象的虚函数表指针不是同⼀个,就像基类对象的成员和派生类对象中的基类对象成员也独立的。
- 3.派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址。
- 4.派生类的虚函数表中包含,(1)基类的虚函数地址,(2)派生类重写的虚函数地址完成覆盖,派生类自己的虚函数地址三个部分。
- 5.虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个0x00000000标记。(这个C++并没有进行规定,各个编译器自行定义的,vs系列编译器会在后面放个0x00000000 标记,g++系列编译不会放)
演示:
#include <iostream>
#include <string>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}virtual void Func1(){cout << "Func1()" << endl;}
protected:string _name;
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "买票-打折" << endl;}virtual void Func2(){cout << "Func2()" << endl;}void Func3(){cout << "Func3()" << endl;}
protected:int _id;
};int main()
{Person p1;Student s1;return 0;
}
代码说明:
- 这里定义了两个类,基类是Person,Person中定义了两个虚函数:BuyTicket和Func1。
- 派生类 Student 中有三个函数,虚函数 BuyTicket 是重写的,虚函数 Func2 是自己新增的,另外还有一个普通的函数 Func3。
监视窗口:
- 首先,红色的线地址不一样,这与上面第一、二条对应,父类与子类的虚表地址是不一样的,如果有多个子类,子类的虚表地址是一样的(这里没有演示可以自行验证)
- 绿色的线地址不一样,这与上面第三条对应,子类重写了父类虚函数,那么子类中该虚函数地址是不一样的。
- 蓝色的线地址相同,因为这个虚函数是直接从父类继承的,没有重写,所以地址一致。
- 这里还有一个问题,就是子类对象 s1 中还有一个虚函数 Func2 和一个普通函数 Func3,Func3 肯定不在虚表中,可是为什么 Func2 也不在呢?其实这是VS出于某种原因故意不显示的,但是我们根据上面第5条,可以在内存窗口看到 Func2 的地址
内存窗口:
- 我们在内存窗口输入s1的虚表地址就能看到:
- 红色的线对应的就是 BuyTicket 虚函数,绿色对应的就是 Func1 虚函数,(注意:因为是小端存储,所以地址是倒序),根据上面第5条虚函数表末尾会以 0x00000000 标记,我们可以大胆猜测画蓝色线的地址就是 Func2。
最后补充两条:
- 6.虚函数存在哪的?虚函数和普通函数一样的,编译好后是一段指令,都是存在代码段的,只是虚函数的地址又存到了虚表中。
- 7.虚函数表存在哪的?这个问题严格说并没有标准答案C++标准并没有规定,vs下是存在代码段(常量区)。
总结
以上就是本文的全部内容了,感谢你的支持!
相关文章:
【C++】多态
目录 文章目录 前言 一、多态的概念 二、多态的定义及实现 三、重载/重写/隐藏的对比 四、纯虚函数和抽象类 五、多态的原理 总结 前言 本文主要讲述C中的多态,涉及的概念有虚函数、协变、纯虚函数、抽象类、虚表指针和虚函数表等。 一、多态的概念 多态分…...
CentOS 8 停止维护后通过 rpm 包手动安装 docker
根据 Docker官方文档 的指引,进入 Docker rpm 包下载的地址,根据自己系统的架构和具体版本选择对应的路径 这里使用 Index of linux/centos/7/x86_64/stable/ 版本,根据 docker 官方的给出的安装命令选择性的下载对应的 rpm 包 最终使用 yum …...
STT-MRAM CIM 赋能边缘 AI:高性能噪声鲁棒贝叶斯神经网络宏架构详解
引言 近年来,基于卷积神经网络(CNN)和视觉转换器(ViT)的存算一体(CIM)边缘AI设备因其低延迟、高能效、低成本等性能受到越来越广泛的关注。然而,当环境中存在噪声时(例如…...
Performance Hub Active Report
Performance Hub 是 Oracle Enterprise Manager Database Express (EM Express) 中的一项功能,可提供给定时间范围内所有性能数据的新整合视图。用户可以使用 Database Express 页面顶部的时间选择器选择时间范围,详细信息选项卡将…...
小白闯AI:Llama模型Lora中文微调实战
文章目录 0、缘起一、如何对大模型进行微调二、模型微调实战0、准备环境1、准备数据2、模型微调第一步、获取基础的预训练模型第二步:预处理数据集第三步:进行模型微调第四步:将微调后的模型保存到本地4、模型验证5、Ollama集成部署6、结果测试三、使用总结AI是什么?他应该…...
【数学建模】TOPSIS法简介及应用
文章目录 TOPSIS法的基本原理TOPSIS法的基本步骤TOPSIS法的应用总结 在 多目标决策分析中,我们常常需要在多个选择中找到一个最优解。 TOPSIS(Technique for Order Preference by Similarity to Ideal Solution)法是一个广泛应用的决策方法…...
优选算法训练篇08--力扣15.三数之和(难度中等)
目录 1.题目链接:15.三数之和 2.题目描述: 3.解法(排序双指针) 1.题目链接:15.三数之和 2.题目描述: 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &…...
【docker】--- 详解 WSL2 中的 Ubuntu 和 Docker Desktop 的区别和关系!
在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。【WSL 】--- Windows11 迁移 WSL 超详细指南 —— 给室友换一个宿舍! 开发环境一、引…...
RAG 架构地基工程-Retrieval 模块的系统设计分享
目录 一、知识注入的关键前奏——RAG 系统中的检索综述 (一)模块定位:连接语言模型与知识世界的桥梁 (二)核心任务:四大关键问题的协调解法 (三)系统特征:性能、精度…...
解决stm32引脚如果选择输入模式
1. 输入模式分类 STM32的GPIO输入模式主要分为以下四种: 浮空输入(Floating Input / Input Floating) 上拉输入(Input Pull-Up) 下拉输入(Input Pull-Down) 模拟输入(Analog Inp…...
Java 填充 PDF 模版
制作 PDF 模版 安装 OnlyOffice 从 OnlyOffice 官网下载 OnlyOffice Desktop,安装过程很简单,一路下一步即可。用 OnlyOffice 制作 PDF 模版(表单) 使用 OnlyOffice 表单设计器,制作表单,如下图 注意命名…...
Maven安装与环境配置
首先我们先介绍一些关于Maven的知识,如果着急直接看下面的安装教程。 目录 Maven介绍 Maven模型 Maven仓库 Maven安装 下载 安装步骤 Maven介绍 Apache Maven是一个项目管理和构建工具,它基于项目对象模型(Project Object Model , 简称: POM)的概念…...
鸿蒙HarmonyOS NEXT应用崩溃分析及修复
鸿蒙HarmonyOS NEXT应用崩溃分析及修复 如何保证应用的健壮性,其中一个指标就是看崩溃率,如何降低崩溃率,就需要知道存在哪些崩溃,然后对症下药,解决崩溃。那么鸿蒙应用中存在哪些崩溃类型呢?又改如何解决…...
基于PySide6的CATIA自动化工具开发实战——空几何体批量清理系统
一、功能概述 本工具通过PySide6构建用户界面,结合PyCATIA库实现CATIA V5的自动化操作,提供两大核心功能: 空几何体清理:智能识别并删除零件文档中的无内容几何体(Bodies)空几何图形集清理࿱…...
【CSS文字渐变动画】
CSS文字渐变动画 HTML代码CSS代码效果图 HTML代码 <div class"title"><h1>今天是春分</h1><p>正是春天到来的日子,花都开了,小鸟也飞回来了,大山也绿了起来,空气也有点嫩嫩的气息了</p>…...
Mysql深分页的解决方案
在数据量非常大的情况下,深分页查询则变得很常见,深分页会导致MySQL需要扫描大量前面的数据,从而效率低下。例如,使用LIMIT 100000, 10时,MySQL需要扫描前100000条数据才能找到第10000页的数据。 在MySQL中解决深分页…...
使用pycel将Excel移植到Python
1.适用需求 有些工作可能长期适用excel来进行公式计算,当需要把工作流程转换为可视化界面时,开发人员不懂专业逻辑,手动摸索公式很大可能出错,而且费时费力 2.可用工具及缺点 pandas 方便进行数据处理,支持各种格…...
Apache Tomcat CVE-2025-24813 安全漏洞
Apache Tomcat CVE-2025-24813被广泛利用,但是他必须要满足两个点: 1.被广泛的使用,并且部署在服务器中。 2.漏洞必须依赖在服务器中的配置。 并且漏洞补丁已经发布。 漏洞攻击方式: CVE-2025-24813 是 Apache Tomcat 部分 PUT…...
Spring常用注解汇总
1. IOC容器与Bean管理 注解说明示例Component通用注解,标记类为Spring Bean Component public class MyService { ... } Controller标记Web控制器(应用在MVC的控制层) Controller public class UserController { ... } Service标记业务逻辑层…...
【CXX-Qt】2.1.1 为 WebAssembly 构建
CXX-Qt 及其编写的应用程序可以编译为 WebAssembly,但存在一些限制。以下是关于如何为 WASM 目标构建的详细说明。 你需要安装 Qt for WebAssembly。下一篇将展示已测试的版本。 此外,如果尚未完成,请从此处克隆 emsdk git 仓库。 使用正确…...
MySql创建分区表并且按月分区
前言 在mysql中,按月份分区,再使用分区字段时间来查询数据将会很快,因为这样只需要扫描指定的分区。因此,在处理大量数据时,使用分区表是一个非常好的选择。 1、创建表,并使用RANGE COLUMNS分区 按创建时间…...
YOLO-UniOW: 高效通用开放世界目标检测模型【附论文与源码】
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
Flink实战教程从入门到精通(基础篇)(一)Flink简介
目录 一、Flink 二、谁在用Flink? 三、Flink特点 1、批流统一 2、性能卓越 3、规模计算 4、生态兼容性 5、高容错性 四、Flink介绍 1、无界数据 2、有界数据流 3、有状态流处理 五、Flink的发展历史 六、Flink的核心特点 1、高吞吐和低延迟 2、结果的准确性 …...
C/C++编程:Openssl使用 Windows安装包32和64位 RSA加密/解密、AES-GCM加密/解密以及ECDSA签名/验证示例
Openssl的头文件和库 C/C使用openssl,需要openssl的头文件和库,这些都在安装包里。从http://slproweb.com/products/Win32OpenSSL.html下载已经编译好的包含 lib 和 include 文件的安装包。 也可以从官网下载源码,再编译成安装包࿰…...
Es6新特性
1. let 和 const 概念 let:用于声明 块级作用域 的变量。const:用于声明 块级作用域 的常量,声明后不可重新赋值(但可以修改对象的属性或数组的内容)。 原理 JavaScript 在 ES5 中只有全局作用域和函数作用域&…...
大数据学习(80)-数仓分层
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
StarRocks 升级注意事项
前段时间升级了生产环境的 StarRocks,从 3.3.3 升级到了 3.3.9,期间还是踩了不少坑所以在这里记录下。 因为我们的集群使用的是存算分离的版本,也是使用官方提供的 operator 部署在 kubernetes 里的,所以没法按照官方的流程进入虚…...
Java 大视界 -- Java 大数据分布式计算中的通信优化与网络拓扑设计(145)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
LabVIEW软件长时间运行导致蓝屏问题排查与优化
计算机在长时间运行LabVIEW或其他软件后出现蓝屏(BSOD),通常由硬件资源耗尽、驱动冲突或软件内存泄漏引发。本文提供从日志分析到根本性优化的全流程解决方案,确保系统稳定运行。 一、蓝屏记录查询方法 1. 查看Windows事件日志 操…...
【机密计算顶会解读】11:ACAI——使用 Arm 机密计算架构保护加速器执行
导读:本文介绍ACAI,其构建一个基于CCA的解决方案,使得机密虚拟机能够安全地使用加速器,同时保持与现有应用程序的兼容性和安全性,能够实现对加速器的安全访问。 原文链接:ACAI: Protecting Accelerator Ex…...
【WRF模拟】WPS预处理设置生成文件地址
目录 WPS 运行 geogrid.exe在 namelist.wps 中指定 geogrid.exe 输出路径WPS 运行 ungrid.exe方法 1:在 namelist.wps 中指定输出路径方法 2:手动移动 FILE:* 文件方法 3:使用环境变量 WPS_UNGRIB_OUTPUT(不推荐)另:设置文件链接地址WPS 运行 metgrid.exe方法 1:在 name…...
Midjourney使用教程—2.作品修改
当您已生成第一张Midjourney图像的时候,接下来该做什么?了解我们用于修改图像的工具!使用 Midjourney 制作图像后,您的创意之旅就不会止步于此。您可以使用各种工具来修改和增强图像。 一、放大操作 Midjourney每次会根据提示词…...
基于 ABAP RESTful 应用程序编程模型开发 OData V4 服务
一、概念 以个人图书管理为例,创建一个ABAP RESTful 应用程序编程模型项目。最终要实现的效果: 用于管理书籍的程序。读取、修改和删除书籍。 二、Data Model-数据模型 2.1 创建项目基础数据库表 首先,创建一个图书相关的表,点…...
微信小程序登陆之反向代理
一.背景 在互联网架构中,反向代理是连接客户端与后端服务的核心组件。它的核心价值在于: 安全性:隐藏内部服务细节,防止直接暴露到公网。 负载均衡:分散请求到多个后端实例,提升吞吐量。 SSL终止&#x…...
[解决] PDF转图片,中文乱码或显示方框的解决方案
在Java开发中,将PDF文件转换为图片是一项常见的需求,但过程中可能会遇到中文乱码或显示方框的问题。本文将深入探讨这一问题,并提供详细的解决方案,帮助开发者顺利地完成PDF到图片的转换。 一、问题现象 在使用Java库(如Apache PDFBox)将PDF转换为图片时,如果PDF文件中…...
面试康复训练-SQL语句
一,数据库操作 1查看所有库 show databases; --查看所有库2使用数据库 use 数据库名; --使用数据库; 3查看当前使用数据库 select database(); --查看当前使用的数据库 4 创建数据库 create databse 数据库名 charsetutf8; --创建数据库 5删…...
经典面试题:C/C++中static关键字的三大核心作用与实战应用
一、修饰局部变量:改变生命周期,保留跨调用状态 核心作用: 延长生命周期:将局部变量从栈区移至静态存储区(数据段或BSS段),生命周期与程序一致保留状态:变量在函数多次调用间保…...
Linux固定IP方法(RedHat+Net模式)
1、查看当前网关 ip route | grep default 2、配置静态IP 双击重启 3、验证...
JVM 学习前置知识
JVM 学习前置知识 Java 开发环境层次结构解析 下图展示了 Java 开发环境的层级关系及其核心组件,从底层操作系统到上层开发工具,逐步构建完整的开发与运行环境: 1. 操作系统(Windows, MacOS, Linux, Solaris) 作用&…...
数据结构---图的深度优先遍历(DFS)
一、与树的深度优先遍历之间的联系 1.类似于树的先根遍历。 递归访问各个结点: 2.图的深度优先遍历 先设置一个数组,初始值全部设置为false,先访问一个结点,在用一个循环,依次检查和这个结点相邻的其他结点,…...
QPrintDialog弹出慢的问题
开发环境 操作系统: openkylin2qt版本 : 5.15.10排查过程 首先看下问题的现象, 问题现象 复现问题的demo很简单,只能是从跟踪qt代码方面入手 void MainWindow::on_pushButton_clicked(){QPrinter printer;QPrintDialog dialog(&printer,this);dialog.exec();} 现在需要找一…...
QT-LINUX-Bluetooth蓝牙开发
BlueToothAPI QT-BlueToothApi Qt Bluetooth 6.8.2 官方提供的蓝牙API不支持linux。 D-Bus的API实现蓝牙 确保系统中安装了 BlueZ(版本需≥5.56),并且 Qt 已正确安装并配置了 D-Bus 支持。 默默看了下自己的版本.....D-BUS的API也不支持。 在 D-Bus 中,org 目录是 D-Bus…...
kvm虚拟机的基本使用
[rootkvm ~]# virsh destroy 虚拟机名 #关闭虚拟机 [rootkvm ~]# virsh undefine 虚拟机名 #删除虚拟机 [rootkvm ~]# virsh start 虚拟机名 #开启虚拟机 [rootkvm ~]# virsh console 虚拟机名 #登录虚拟机 [rootkvm ~]# virsh list --all …...
K8S中若要挂载其他命名空间中的 Secret
在Kubernetes(k8s)里,若要挂载其他命名空间中的Secret,你可以通过创建一个 Secret 的 ServiceAccount 和 RoleBinding 来实现对其他命名空间 Secret 的访问,接着在 Pod 中挂载这个 Secret。下面是详细的步骤和示例代码…...
【Java SE】抽象类/方法、模板设计模式
目录 1.抽象类/方法 1.1 基本介绍 1.2 语法格式 1.3 使用细节 2. 模板设计模式(抽象类使用场景) 2.1 基本介绍 2.2 具体例子 1.抽象类/方法 1.1 基本介绍 ① 当父类的某些方法,需要声明,但是又不确定如何实现时ÿ…...
如何理解java中Stream流?
在Java中,Stream 是 Java 8 引入的一个强大API,用于处理集合(如 List、Set、Map 等)数据的流式操作。它提供了一种声明式、函数式的编程风格,可以高效地进行过滤、映射、排序、聚合等操作。 Stream 的核心概念 流&…...
QT编程之数据库开发
一、架构层次 用户接口层 QSqlQueryModel:管理SQL查询结果,提供表格数据模型用于展示QSqlTableModel:支持直接操作数据库表(增删改查)QSqlRelationalTableModel:支持带外键关联的复杂表…...
【10】高效存储MongoDB的用法
目录 一、什么是MongoDB 二、准备工作 (1)安装MongoDB (2)安装pymongo库 三、连接MongoDB 四、指定数据库 五、指定集合 六、插入数据 (1) insert 方法 (2)insert_one(…...
使用Qdrant等其他向量数据库时需要将将numpy 数组转换为列表 确保数据能被正确处理和序列化,避免类型不兼容的问题。
在使用Qdrant等其他向量数据库时需要 转换 numpy 数组为列表主要是为了确保数据能被正确处理和序列化,避免类型不兼容的问题。具体原因如下: 序列化兼容性: 很多数据库接口、API 或者 JSON 序列化工具只能处理 Python 的内置类型(…...
mayfly-go开源的一站式 Web 管理平台
mayfly-go 是一款开源的一站式 Web 管理平台,旨在通过统一的界面简化 Linux 服务器、数据库(如 MySQL、PostgreSQL、Redis、MongoDB 等)的运维管理。以下从多个维度对其核心特性、技术架构、应用场景及生态进行详细解析: 一、核心…...