C++进阶之——多态
1. 多态的概念
多态是用来描述这个世界的
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。
这里就很厉害了,能够实现特殊处理,本文章就是来仔细解释多态的原理
举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人 买票时是优先买票。
再举个栗子: 最近为了争夺在线支付市场,支付宝年底经常会做诱人的扫红包-支付-给奖励金的 活动。那么大家想想为什么有人扫的红包又大又新鲜8块、10块...,而有人扫的红包都是1毛,5 毛....。其实这背后也是一个多态行为。支付宝首先会分析你的账户数据,比如你是新用户、比如 你没有经常支付宝支付等等,那么你需要被鼓励使用支付宝,那么就你扫码金额 = random()%99;比如你经常使用支付宝支付或者支付宝账户中常年没钱,那么就不需要太鼓励你 去使用支付宝,那么就你扫码金额 = random()%1;
总结一下:同样是扫码动作,不同的用户扫 得到的不一样的红包,这也是一种多态行为。ps:支付宝红包问题纯属瞎编,大家仅供娱乐。
2. 多态的定义及实现
2.1多态的构成条件
1. 必须(父类)通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
这两点很重要!!!
多态:不同对象传递过去,调用不同的函数
多态调用看指向的对像(不管指向的对象就是基类还是派生类,都去虚基表里面获得函数地址,基类和派生类里面存的地址是不同的)
普通对象看当前的类型(而且在编译的时候就可以知道函数的地址,直接去call就可以了)
下面就是具体场景
2.2虚函数
简单来说就是被virtual修饰的函数就是虚函数
! 这里不要与虚继承搞混掉,两者没有半毛钱关系
只有成员函数才能变为虚函数(能够进行重写)
2.3虚函数的重写
重写的条件:虚函数+三同(返回值,函数名,参数)(有两个例外)
满足条件就称:子类的虚函数重写了基类的虚函数。
虚函数重写的两个例外:
1协变(基类和派生类的返回值不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指 针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:virtual A* f() {return new A;}
};
class Student : public Person {
public:virtual B* f() {return new B;}
};
这里虽然返回值不同,但是还是构成重写
还有一点注意,父用父,子用子,用别的父子关系的类也可以
2. 析构函数的重写(基类与派生类析构函数的名字不同)
只要父类的析构函数加上virtual,就能构成重写
因为析构函数的函数名字都被编译器处理为destructor 这个统一的名字,有老铁就要问了,为什么要处理成这个统一的名字呢?
显而易见就是为了让他们构成重写,因为在有些场合下,必须要用多态调用,多态的其中一个条件就是重写的虚函数
下面就是这种经典场合
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }virtual ~Person() { cout << "~Person()" << endl; }
};class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }~Student() {cout << "~Student()" << endl;delete[] ptr;}protected:int* ptr = new int[10];
};int main()
{Person* p = new Person;p->BuyTicket();delete p;p = new Student;p->BuyTicket();delete p; // p->destructor() + operator delete(p)// 这里我们期望p->destructor()是一个多态调用,而不是普通调用不然的话就会出现内存泄露哦return 0;
}
再次提醒 delete p== p->destructor()+operator delete(p) ,先去调用析构函数再去释放空间
2.4 C++11 override 和 final
从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数 名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有 得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮 助用户检测是否重写。
1. final:修饰虚函数,表示该虚函数不能再被重写
若不想让一个类被继承的话有哪些办法?
(1)这个final 也可以写在Car的后面,表示这个基类不能被继承(c++11)
(2)基类构造函数私有或者析构函数放到私有(这样派生类就不能去调用基类的构造或者析构函数去处理数据)
那么问题来了,你的构造函数和析构函数都放到私有了,你怎么创建对象呢?
解决办法(很经典):虽然在外部不能调用构造,但是类里面可以啊
class A
{
public:static A Get_A(){return A();}
private:
A(int a = 1):_a(a){ }int _a;
};class B :public A
{
protected:int _b;};
int main()
{A a = A::Get_A();return 0;
}
析构函数放在私有就不再演示了
2. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
2.5 重载、覆盖(重写)、隐藏(重定义)的对比
3. 抽象类
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。
包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象(没有实体)。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生 类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
虚函数只有重写了才有意义,纯虚函数:强制派生类重写虚函数
这里有个特例,虽然不可以用父类创建对象(子类重写了纯虚函数的前提),但是可以用父类的指针和引用,这原因很简单,就是为了构成多态调用
4. 多态的原理
4.1虚函数表
虚表(相当于函数指针数组)不等于虚基表(存的是偏移量)
解释多态的原理就要去仔细解释虚函数表
虚函数本质放到代码段里面,虚函数表中存的是虚函数的地址
普通调用在编译的时候就会确定地址
若符合多态调用,就会在运行的时候到指定对象的虚基表中找调用函数的地址,若此时指向的对象是派生类,因为虚基表里面存的函数的地址是派生类里面的函数地址,这样就解释了为啥子不同对象调用的时候会表现出特殊处理
// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
一般人可能就想着,函数不存在类里面啊,那答案就是1啊,可是正确答案是8(x86环境下),这是因为类里面还存着一个叫_vfptr(全名叫virtual function table ptr)的指针,指向func1()这个函数
对象中的这个指针我们叫做虚函数表指针。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数 的地址要被放到虚函数表中,虚函数表也简称虚表,。那么派生类中这个表放了些什么呢?我们 接着往下分析
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;}
private:int _d = 2;
};
int main()
{Base b;Derive d;return 0;
}
还有一点:同类型的类公用一个虚表
可见同类型的虚表地址相同
通过观察和测试,我们发现了以下几点问题:
1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,虚 表指针也就是存在部分的另一部分是自己的成员。
2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表 中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数 的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函 数,所以不会放进虚表。
4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。
5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生 类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己 新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
而且注意:派生类新增加的虚函数不会在监视窗口看见
这时候可以用地址来查看
可以看见fun3实际是存在的
而且vs有一个小问题就是当你改代码后(没有重新生成解决方案的话,虚表的地址不会变,而且虚表最后一个位置不会被赋值为nullptr)
问题来了,你怎么知道这个位置就是func3呢?我们只是猜测这个地址是func3的地址,要想办法来验证才可以
在此之前先让我们验证一下虚表存在的位置
可能位置 1栈 2 堆 3 静态区(数据段)4 常量区(代码段)
可以写一个小程序,来比较虚表的地址和以上哪一个地址相差小
class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }virtual void func1() {};virtual void func2(){};int _a=1;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }virtual void func2(){};virtual void func3(){};int _b = 1;
};int main()
{Person ps;Student st;int a = 0;printf("栈:%p\n", &a);int* b = new int;printf("堆:%p\n", b);static int c = 0;printf("静态区:%p\n", &c);const char* str = "hallow world";printf("常量区:%p\n", str);printf("虚表1:%p\n", *((int*) & ps));printf("虚表2:%p\n",*((int*) & st));return 0;
}
答案显而易见,就是存在常量区(也就是代码段)
有一点要说明:类里面是先存的是虚表的地址,是一个指针,在x86环境下是4个字节,因为这个虚表的地址和类的地址起始位置相同,我们就把类的地址强转成int* (访问4个字节),这样就可以得到虚表的地址了
解下来就是验证这个地址就是func3,因为_vfptr地址指向的是一个数组,并且是一个函数指针数组
我们要想办法得到这个地址,然后去直接用地址调用看看func3是否被调用
这里回忆一下函数指针数组
下面是测试代码
//因为函数指针数组写起来比较麻烦
typedef void (*FUNC_PTR)();
void PrintVFT(FUNC_PTR table[])
{for (size_t i = 0; table[i] != nullptr; i++){printf("[%d]:%p->", i, table[i]);FUNC_PTR f = table[i];f();}cout << endl;
}class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }virtual void func1() {cout << "func1()" << endl;};virtual void func2(){cout << "func2()" << endl;};int _a=1;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }virtual void func2(){cout << "func2()" << endl;};virtual void func3(){cout << "func3()" << endl;};int _b = 1;
};int main()
{Person ps;Student st;//这里转化成int可以直接将地址存起来,用的时候在强转int pptr = *((int*) & ps);int sptr = *((int*) & st);PrintVFT((FUNC_PTR*)pptr);PrintVFT((FUNC_PTR*)sptr);//这里将地址转化为指针数组的地址return 0;
}
6. 这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的? 答:虚函数存在 虚表,虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。注意 虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是 他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。那么虚表存在哪的 呢?实际我们去验证一下会发现vs下是存在代码段的,Linux g++下大家自己去验证?
我们在重新说一遍:多态的定义,并且引出一些问题
1. 必须(父类)通过基类的指针或者引用调用虚函数
问题一:为什么不能是子类的指针或引用 ?
因为只有父类的指针或引用才能指向父类和子类(发生切片,指向的任然是子类的一部分)
问题二:为什么不能是父类对象?
子类赋值给父类会进行切片,但是不会拷贝虚表
如果拷贝虚表,那么父类对象虚表中是父类虚函数还是子类就不确定了就乱套了
但是派生类的虚表相当于父类的虚表拷贝过来,若有重写就进行覆盖
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
5. 单继承和多继承关系中的虚函数表
需要注意的是在单继承和多继承关系中,下面我们去关注的是派生类对象的虚表模型,因为基类 的虚表模型前面我们已经看过了,没什么需要特别研究的
5.1 单继承中的虚函数表
我们之前就测试过了,发现子类里面的虚函数不会在监视窗口显示,这也是一个vscode的小bag,不过我们已经从内存里面看到了子类的虚函数确实存在,而且也通过地址调用了函数
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;FUNC_PTR* vTableb1 = (FUNC_PTR*)(*(int*)&d);PrintVFT(vTableb1);
下面一步比较巧妙因为先存Base1,我们可以跳过Base1去打印Base2的虚表FUNC_PTR* vTableb2 = (FUNC_PTR*)(*(int*)((char*)&d + sizeof(Base1)));PrintVFT(vTableb2);return 0;
}
问题来了,派生类对func1进行重写,按我们的理解,两个Base虚表里面的func1地址应该相同啊(都进行覆盖),为啥func1地址不同?func1只有一个,肯定地址是不变的
这里原因就是编译器对其进行了封装
要通过汇编才能看到真正的原因
也就是相当于Base1里面存了func1重写的地址(这样只用存一份),Base2想用就通过地址偏移去Base1里面调用
5.3菱形继承中的虚函数表
6. 继承和多态常见的面试问题
概念
下面一题就很有难度了
这里要搞明白的点就是
1:继承父对象相当于把他当作子类的一个成员
2:我们去不同类里面去调用函数,都是通过传this指针,通过this指针去调用函数,可见当p去调用test传的this是B*类型,而test只在A中有,我们说过继承的父对象相当于一个成员,这里去A中调test的时候,在A里面this肯定是A*这个类型,说明B*转化为A*发生切片,这时候test()里面调用的又是一个重写的虚函数func()(缺省值不同不影响重写),符合多态调用,那么就会去调B里面的func
3可是那么问题来了,缺省值不同到底用哪个呢?,这里又要知道一个知识点,重写是指重写的实现,说白了就是壳子用的是父类的,函数里面代码逻辑是用的子类的,那么答案为B
问答
1. 什么是多态?
要分为两点来答
1静态多态:函数重载
2动态多态:继承中重写的虚函数+父类指针调用
更方便和灵活多种形态的调用
2. 什么是重载、重写(覆盖)、重定义(隐藏)?
在文章中已经给出详细图片
3. 多态的实现原理?
虚函数表
4. inline函数可以是虚函数吗?
可以,不过编译器就忽略inline属性,这个函数就不再是 inline,因为虚函数要放到虚表中去。
inline 函数没有地址,不会去call这个函数,直接展开,注意:类里面定义默认都是内联
5. 静态成员可以是虚函数吗?
不能,因为静态成员函数没有this指针,使用类型::成员函数 的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。
6. 构造函数可以是虚函数吗?
不能,因为对象中的虚函数表指针是在构造函数初始化列表 阶段才初始化的。那你放进去,还怎样调用构造函数呢?
7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
可以,并且最好把基类的析 构函数定义成虚函数。因为我们要使析构的时候也满足多态调用,要不然析构的时候子类去用了父类的析构,会有内存泄漏
8. 对象访问普通函数快还是虚函数更快?
首先如果是普通对象,是一样快的。如果是指针 对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函 数表中去查找。
9. 虚函数表是在什么阶段生成的,存在哪的?
虚函数表是在编译阶段就生成的(在构造函数里面初始化),一般情况 下存在代码段(常量区)的。
10. C++菱形继承的问题?虚继承的原理?
问题我在文章里面已经解释清楚了,虚继承的原理使虚基表
注意这里不要把虚函数表和虚基 表搞混了。
11. 什么是抽象类?抽象类的作用?
参考(3.抽象类)。抽象类强制重写了虚函数,另外抽 象类体现出了接口继承关系。
大家可以去积极思考一下哦,都是大厂考过的题
相关文章:
C++进阶之——多态
1. 多态的概念 多态是用来描述这个世界的 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。 这里就很厉害了,能够实现特殊处理,本文章就是来仔细…...
第13项三期,入组1123例:默沙东启动TROP2 ADC+PD-1子宫内膜癌头对头临床
Umabs DB作为目前全球最全面的抗体药物专业数据库,收录全球近10000个从临床前到商业化阶段抗体药物,涉及靶点1600,涉及疾病种类2400,研发机构2900,覆盖药物蛋白序列、专利和临床等多种专业信息。Umabs DB药物数据库已正…...
政务服务智能化改造方案和案例分析
政务服务智能化改造方案和案例分析 一、引言 在数字化时代浪潮的推动下,政务服务智能化改造已成为提升政府服务效能、优化营商环境、增强民众满意度的关键举措。传统政务服务模式存在流程繁琐、信息孤岛、办理效率低等问题,难以满足现代社会快节奏发展和…...
15.日志分析入门
日志分析入门 第一部分:日志分析基础第二部分:日志分析方法与工具第三部分:日志分析实践总结 目标: • 理解日志分析在网络安全中的作用 • 掌握日志的基本类型和分析方法 • 通过实践初步体验日志分析的过程 第一部分ÿ…...
EPSG:3857 和 EPSG:4326 的区别
EPSG:3857 和 EPSG:4326 是两种常用的空间参考系统,主要区别在于坐标表示方式和应用场景。以下是它们的核心差异: 1. 坐标系类型 EPSG:4326(WGS84) 地理坐标系(Geographic Coordinate System),基…...
Python Cookbook-7.2 使用 pickle 和 cPickle 模块序列化数据
任务 你想以某种可以接受的速度序列化和重建Python 数据结构,这些数据既包括基本Python 对象也包括类和实例。 解决方案 如果你不想假设你的数据完全由基本 Python 对象组成,或者需要在不同的 Python 版本之间移植,再或者需要将序列化后的…...
Java学习手册:Spring 多数据源配置与管理
在实际开发中,有时需要连接多个数据库,例如,一个系统可能需要从不同的数据库中读取和写入数据。Spring 提供了多种方式来配置和管理多数据源,以下将介绍常见的配置和管理方法。 一、多数据源配置 在 Spring 中,可以通…...
六、shell脚本--正则表达式:玩转文本匹配的“万能钥匙”
想象一下,你需要在一大堆文本(比如日志文件、配置文件、网页代码)里查找符合某种特定模式的字符串,而不是仅仅查找固定的单词。比如说: 找出所有的电子邮件地址 📧。找到所有看起来像电话号码 Ὅ…...
Gradio全解20——Streaming:流式传输的多媒体应用(4)——基于Groq的带自动语音检测功能的多模态Gradio应用
Gradio全解20——Streaming:流式传输的多媒体应用(4)——基于Groq的带自动语音检测功能的多模态Gradio应用 本篇摘要20. Streaming:流式传输的多媒体应用20.4 基于Groq的带自动语音检测功能的多模态Gradio应用20.4.1 组件及配置1.…...
力扣hot100 (除自身以外数组的乘积)
238. 除自身以外数组的乘积 中等 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除…...
LFU算法解析
文章目录 LFU缓存中关键变量的访问与更新机制1. min_freq - 最小频率访问时机更新时机更新示例 2. capacity - 缓存容量访问时机更新时机访问示例 3. key_to_node - 键到节点的映射访问时机更新时机更新示例 4. freq_to_dummy - 频率到链表哑节点的映射访问时机更新时机更新示例…...
RHCSA笔记2
RHCSA基础命令 (一)命令格式 (1)命令名【选项】【参数】 选项:决定命令执行的方式,通常有个-或--开头 参数:决定命令作用的目标(目录,文件,磁盘ÿ…...
JavaSE核心知识点01基础语法01-02(基本数据类型、运算符、运算符优先级)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 JavaSE核心知识点01基础语法01-02࿰…...
FOC算法开环控制基础
1. 为什么要有FOC算法 先看看从有刷电机到无刷电机的简单介绍,如下图1,通电螺线圈会产生磁场,这个磁场会产生N级和S级,然后这个电磁铁就可以吸引永磁体,S级吸引N级,N级吸引S级,通俗的来说&…...
进程间通信——管道
概念 进程间通信(Inter-Process Communication,简称 IPC)是指在不同进程之间进行数据交换和信息传递的机制。它的目的主要有4种: 数据传输:一个进程需要将它的数据发送给另一个进程资源共享:多个进程之间…...
五一作业-day02
文章目录 1. 每日基操2. 模拟故障2.1 **remove regular empty file 是否删除普通文件(空的)?**2.2 **is a directory xxx是一个目录**2.3 **xxx not a directory 不是一个目录**2.4 Cant open file for writing2.5 **No write since last change** 3. 习题4. **进阶习题** 1. …...
Springclound常用五大组件及其使用原理
注册中心Eureka Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。 提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约 …...
Qt 显示QRegExp 和 QtXml 不存在问题
QRegExp 和 QtXml 问题 在Qt6 中 已被弃用; 1)QRegExp 已被弃用,改用 QRegularExpression Qt5 → Qt6 重大变更:QRegExp 被移到了 Qt5Compat 模块,默认不在 Qt6 核心模块中。 错误类型解决方法QRegExp 找不到改用 Q…...
开元类双端互动组件部署实战全流程教程(第4部分:后台配置系统与参数动态控制)
作者:曾经因为后台配置写错,导致全服进不去房的工程师 组件附带的后台管理系统为 PHP 编写,界面简洁但功能齐全。具备完整的模块划分与权限体系,支持动态参数下发、日志审计、行为数据统计等。 七、前端后台交互流程图与代码示例 …...
MySQL基础关键_008_DDL 和 DML(一)
目 录 一、DDL 1.创建表 (1)语法格式 (2)实例 2.查看建表语句 (1)语法格式 (2)实例 3.修改表名 (1)语法格式 (2)实例 4.新…...
基于SpringBoot + Vue 的火车票订票系统
包含: [1]源码✔ 数据库文件✔ [2]万字文档✔ [3]视频与图文配置教程✔ 功能描述: 本系统包含管理员、用户两个角色。 管理员:用户管理、新闻公告管理、车辆管理、车站及路线管理、留言建议管理、车次信息管理 用户:购票操作、查…...
飞致云开源社区月度动态报告(2025年4月)
自2023年6月起,中国领先的开源软件公司飞致云以月度为单位发布《飞致云开源社区月度动态报告》,旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况,以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营数据概览&…...
解决跨域的4种方法
00_跨域的概念 浏览器只允许请问具有相同的协议,域名,端口,进行请求,有一个不同,就会拒绝。 01.前后端协商jsonp //jsonp//jsonp 是 json with padding 的缩写,是一种通过 <script> 标签的 src 属性…...
C# 方法(局部函数和参数)
本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 局部函数 正如刚刚所解释的&…...
kotlin 02flow-sharedFlow 完整教程
一 sharedFlow是什么 SharedFlow 是 Kotlin 协程中 Flow 的一种 热流(Hot Flow),用于在多个订阅者之间 共享事件或数据流。它适合处理 一次性事件(如导航、弹窗、Toast、刷新通知等),而不是持续状态。 ✅ …...
数据库原理——E-R图的极速省流理解 例题解析
前言 数据库一节没听,一个小时看书给我大致看懂了 E-R概念模型极速省流版 E-R图的重点: 关系图,三要素——实体、属性、联系 图形标识——矩形、椭圆形、菱形 1.实体和属性也可以放一个框矩形框 2.菱形两层边:弱实体集的联…...
5.4 - 5.5Web基础+c语言拓展功能函数
StringBoot HTTP协议: 规定了浏览器与服务器之间数据传递的规则。 请求协议: 请求数据格式: 请求头和请求体之间有一个空行隔开 响应协议: 响应数据格式: 响应头和响应体之间存在空行隔开。 响应数据设置࿱…...
Java抽象类与接口详解
一、抽象类(Abstract Class) 1. 定义与基本使用 // 抽象类定义 public abstract class Animal {// 抽象方法(无实现)public abstract void makeSound();// 具体方法(有实现)public void sleep() {System.out.println("动物在睡觉");} }// 继承抽象类 class Dog ext…...
网络延时 第四次CCF-CSP计算机软件能力认证
就是求树的直径: 思路:函数代表当前根节点的最长距离 然后遍历保存当前树的所有孩子的最长距离 和次长距离 如果是叶子节点就返回0 在每次获得每个节点的次长距离和最长距离就更新全局直径 最后获得最长距离 Ac代码: #include <bits/stdc.h> using namespa…...
【C++进阶十】多态深度剖析
【C进阶十】多态深度剖析 1.多态的概念及条件2.虚函数的重写3.重写、重定义、重载区别4.C11新增的override 和final5.抽象类6.虚表指针和虚表6.1什么是虚表指针6.2指向谁调用谁,传父类调用父类,传子类调用子类 7.多态的原理8.单继承的虚表状态9.多继承的…...
网络传输中字节序
在小端字节序主机发送数据 0x1234 的情况下,(单字节没有字节序)我们可以分步骤来分析接收端如何解析这个数据: 1. 小端字节序主机的存储方式 在小端字节序中,低地址存储低字节,高地址存储高字节。 数据 0x1234 的字节表示为: 低字节:0x34 高字节:0x12 因此,在小端字…...
前端- ElementPlus入门
1.介绍 Element:是饿了么公司前端开发团队提供的一套基于 Vue3 的网站组件库,用于快速构建网页。 Element 提供了很多组件供我们使用。例如 超链接、按钮、图片、表格等等。 官方网站:一个 Vue 3 UI 框架 | Element Plus 2.步骤 1.安装E…...
AI Agent 要用到的技术
AI 发展是大趋势,以下是目前要用到的一些技术项 不论你从事哪个方向,这个技术栈都有必要学习 LangChainTransformersMicrosoft Semantic KernelLangflowLangGrphLangSmith 学习网站 以下是 LangChain、Transformers、Microsoft Semantic Kernel 的学习…...
# 从零构建一个简单的卷积神经网络:手写数字识别
从零构建一个简单的卷积神经网络:手写数字识别 在深度学习的世界里,卷积神经网络(CNN)是处理图像数据的强大工具。今天,我们将通过一个简单的例子,从零开始构建一个CNN模型,用于手写数字识别。…...
【RK3588嵌入式图形编程】-Cairo-Cairo图形库支持后端
Cairo图形库支持后端 文章目录 Cairo图形库支持后端1、PNG图像后端2、PDF文件后端3、SVG文件后端4、GTK窗口支持Cairo库支持多种后端。在本文中,我们使用Cairo创建PNG图像、PDF文件、SVG文件,并在GTK窗口上绘制。 1、PNG图像后端 在第一个示例中,我们创建一个 PNG 图像。 …...
LCD,LED
本文来源 : 腾讯元宝 LCD(Liquid Crystal Display)液晶显示器 LCD本身并不能发光,而是控制光的传输。 LCD内充满了棒状的液态分子(液晶),这些分子可以形成扭转的螺旋线,弯曲来自显示器背后光源产生的光线或…...
HTML 元素
什么是 HTML 元素? HTML 元素(Element)是构成 HTML 文档的基本单位,它由开始标签、内容和结束标签组成,用于定义网页的结构和内容。元素是 HTML 标记语言的核心概念,每个元素都有特定的语义和用途。 元素…...
(undone) MIT6.S081 2023 学习笔记 (Day11: LAB10 mmap)
url: https://pdos.csail.mit.edu/6.1810/2023/labs/mmap.html 任务0:讲义如下 (完成) mmap和munmap系统调用允许UNIX程序对其地址空间进行精细控制。它们可用于进程间共享内存、将文件映射到进程地址空间,并作为用户级页面错误处理方案的一部分&#x…...
7400MB/s5050TBW完美结合,全新希捷酷玩530R SSD体验评测
7400MB/s&5050TBW完美结合,全新希捷酷玩530R SSD体验评测 哈喽小伙伴们好,我是Stark-C~ 说到希捷酷玩530 SSD,很多硬核进阶玩家应该都知道,或者说正在使用(比如说我~)。 作为希捷大厂旗下高性能SSD的…...
【数据结构与算法】同余计算 哈希表与前缀和问题特征和模板化思路
加减乘负的类同余计算 加乘模情况 两数和模 : (a b) mod m (a mod m b mod m) mod m乘积模 : (a * b) mod m ((a mod m) * (b mod m)) mod m 加模证明如下 : a q1 * m r1 , b q2 * m r2则 a mod m r1 , b mod m r2(a b) mod m ((q1 q2) * m (r1 r2)) mod m …...
TS 交叉类型
很类似于接口继承啊 哈哈哈 使用type关键字声明交叉类型 // 声明交叉类型 type PersonDetail person & Contactlet theshy:PersonDetail { name:the shy,phone:马头 }交叉类型和接口实现对对象的类型注解的区别与联系 如果在接口继承时发生同名属性冲突会报不兼容的错…...
FreeRTOS学习系列·二值信号量
目录 1. 信号量的基本概念 2. 二值信号量 3. 应用场景 4. 运作机制 5. 信号量控制块 6. 常用信号量函数接口API 6.1 创建二值信号量 xSemaphoreCreateBinary() 6.2 信号量删除函数 vSemaphoreDelete() 6.3 信号量释放函数 6.3.1 xSemaphoreGive() 6.3.2 …...
二叉搜索树 AVL树 红黑树 的性质
二叉搜索树 如何判断一棵树是否是二叉搜索树? 1.方法一 左子树的所有节点值 < 当前节点值,右子树的所有节点值 > 当前节点值,左右子树也必须满足 。 2.方法二 中序遍历,得到的序列是有序的 红黑树 红黑树的性质 1.根…...
分析 Docker 磁盘占用
以下是分析 Docker 磁盘占用的详细步骤和工具指南,帮助开发者快速定位和清理冗余数据: 1. 查看 Docker 磁盘使用概览 docker system df 输出说明: TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 15 …...
LLM提示词设计及多轮对话优化策略在心理健康咨询场景中的应用研究
多轮对话策略:设计LLM提示词并优化多轮对话输出 LLM提示词设计及多轮对话优化策略在心理健康咨询场景中的应用研究 摘要 本文针对大语言模型(LLM)在心理健康问题咨询对话场景中的应用,系统研究提示词设计方法及多轮对话优化策略。通过分析提示词核心原理,构建包含任务指…...
Kubernetes(k8s)学习笔记(七)--KubeSphere 最小化安装
前情提要 可视化操作面板对于开发、运维绝对是提升工作效率的一大利器,因此很有必要搭建一套可视化操作来管理Kubernetes。 可视化面板有多种: 1.Kubernetes官方提供的默认面板:dashboard,用处不大,放弃;…...
面向对象与过程介绍
一、面向对象具体介绍 (1)基本概念 1.对象 在面向对象编程中,对象是对现实世界中事物的抽象,它具有状态(属性)和行为(方法)。在 TypeScript 中,我们可以通过类、接口等…...
UniGetUI 使用指南:轻松管理 Windows 软件(包括CUDA)
UniGetUI(前身为 WingetUI)是一款专门为 Windows 10(x64)和 Windows 11 系统打造的图形化包管理器界面工具。它集成了 Winget、Scoop、Chocolatey、Npm、Pip、Cargo、vcpkg、.NET Tool 和 PowerShell 等多种常用包管理器的功能&am…...
使用 NGINX 实现 HTTP Basic 认证ngx_http_auth_basic_module 模块
一、前言 在 Web 应用中,对部分资源进行访问控制是十分常见的需求。除了基于 IP 限制、JWT 验证、子请求校验等方式外,最经典也最简单的一种方式便是 HTTP Basic Authentication。NGINX 提供的 ngx_http_auth_basic_module 模块支持基于用户名和密码的基…...
014枚举之指针尺取——算法备赛
枚举是数据结构与算法中基本的操作,常用于解决序列的区间问题。算法界将"双指针"视为其重要分支,类似地当然还有"三指针",“四指针”,最常见的还是“双指针”,我认为它们应统称为“指针尺取”。 双…...