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

【语法】C++的多态

目录

虚函数的重写:

虚函数

重写(覆盖)

虚函数重写的两个例外:

协变:

析构函数的重写:

练习:

final和override关键字

 抽象类

接口继承和实现继承

虚函数重写的原理:

打印虚函数表:

虚函数表在哪?

动态绑定与静态绑定

 多继承中的虚函数表


面向对象的三大特性是封装,继承,多态

本篇的主要目标就是多态

假如我想实现一个买票系统,普通人就买成人票,学生就买学生票,此时就需要用到多态

class person
{
public:virtual void ticket(){cout << "普通票" << endl;}
};class student : public person
{
public:virtual void ticket(){cout << "学生票" << endl;}//将父类和子类的同名函数定义成虚函数,就会构成重写(覆盖)
};void fun(person &p)//用父类的指针或引用调用
{p.ticket();//如果里面存的是子类对象,那么调用的是子类的函数//如果里面存的是父类对象,那么调用的是父类的函数
}int main()
{student s;person p;fun(s);//传入子类对象,调用子类的函数fun(p);//传入父类对象,调用父类的函数return 0;
}

输出结果:

可以发现多态是在继承的基础上有的

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了
Person。Person对象买票全价,Student对象买票半价。

关于上面代码的知识点,下面一个个讲解

那么在继承中要构成多态还有两个条件:

1. 必须通过父类的指针/引用调用虚函数

因为父类指针/引用可以指向父类对象,也可以指向子类对象,但子类指针/引用不能指向父类对象

2. 被调用的函数必须是虚函数,且必须进行重写

虚函数的重写:

上面也提到了,多态的其中一个条件就是虚函数和重写

虚函数

先来讲讲虚函数

和虚继承(在继承时前面加virtual)很像,就是在成员函数前面加virtual关键字,但不能把他们混为一谈。

虚继承是通过虚基表指针指向虚基表,来存储虚基类的偏移量,而虚函数是通过虚函数表指针指向虚函数表,存储类中所有虚函数的地址,两者可以说毫无联系

虚函数的语法:

class 类名
{virtual 返回值类型 函数名(函数参数){函数体;}
}

需要注意的是,虚函数必须是类中的成员!!! 

重写(覆盖)

学过继承的都知道重定义(隐藏),是指当父类和子类成员变量/函数名相同,就构成重定义(隐藏)

重写的全称是虚函数的重写,即重写的对象必须是虚函数

而重写要求父类和子类的虚函数名/返回值/参数都相同(协变例外,下面会讲)

#include <iostream>
using namespace std;struct base{virtual void print(){cout<<"base"<<endl;}};//struct默认publicstruct derived : public base{virtual void print(){cout<<"derived"<<endl;}};void fun(base* b)//现在取决于指向的对象类型
{b->print();
}
int main()
{base* B;derived d;base b;B = &d;//指向子类对象fun(B);//调用子类虚函数B = &b;//指向父类对象fun(B);//调用父类虚函数return 0;
}

输出结果: 

上面代码中,可以看到,两次调用父类指针的print函数,却输出的不同的值,这就是满足多态之后可以实现的

满足多态:跟指向的对象有关,指向哪个对象就调用它的虚函数

不满足多态:跟调用对象的类型有关,类型是什么就调用谁的虚函数

什么叫不满足多态?

#include <iostream>
using namespace std;struct base{virtual void print(){cout<<"base"<<endl;}};//struct默认publicstruct derived : public base{virtual void print(){cout<<"derived"<<endl;}};void fun(base b)//现在取决于调用对象的类型
{b.print();
}
int main()
{base B;derived d;base b;B = d;//指向子类对象fun(B);B = b;//指向父类对象fun(B);return 0;
}

输出结果:

上面代码中,fun函数的参数从指针换成了传值调用,现在就是取决于调用的类型了,不管B中存的是父类对象还是子类对象,它本身的类型还是父类对象,所以只会调用父类对象的虚函数

虚函数重写的两个例外:

上面提到,重写(覆盖)的条件是类成员函数名/参数/返回值完全一致,但有两个例外

协变:

有时当父类和子类的虚函数返回值不相同时,也可以构成重写

#include <iostream>
using namespace std;struct base{virtual base* print(){cout<<"base"<<endl;return nullptr;}};//struct默认publicstruct derived : public base{virtual derived* print(){cout<<"derived"<<endl;return nullptr;}};void fun(base& b)
{b.print();
}
int main()
{derived d;base b;fun(d);//引用指向子类对象,调用子类的虚函数fun(b);//引用指向父类对象,调用父类的虚函数return 0;
}

上面代码也可以构成重写,这是因为父类的虚函数返回值是父类指针/引用,子类的虚函数返回值是子类指针/引用,这就是协变

并且,构成协变的条件未必必须类自身,别的类的父类和子类也可以

#include <iostream>
using namespace std;struct tmpbase{};
struct tmpderived : public tmpbase{};
struct base{virtual tmpbase* print(){cout<<"base"<<endl;return nullptr;}};//struct默认publicstruct derived : public base{virtual tmpderived* print(){cout<<"derived"<<endl;return nullptr;}};void fun(base& b)
{b.print();
}
int main()
{derived d;base b;fun(d);//引用指向子类对象,调用子类的虚函数fun(b);//引用指向父类对象,调用父类的虚函数return 0;
}

此时也构成协变

析构函数的重写:

虽然子类和父类的析构函数名称不一样,但他们可以构成重写

struct base//struct默认public
{virtual ~base(){cout << "~base\n";}
};struct derived : public base
{virtual ~derived(){cout << "~derived\n";}
};

上面代码中,~base和~derived函数也构成了虚函数的重写

但虚函数重写不是要函数名相同吗?

有一种说法是所有析构函数在编译器的角度来看名称都是相同的(例如都是~或都是destructor),再加上他们没有返回值也没用参数,所以可以构成重写

但这种说法仍然存在一些问题,我还是宁愿相信析构函数在编译器中是没有名字的,可以构成重写是底层的语法支持的,就像子类对象可以赋值给父类对象一样

那实际写程序中我们需不需要让析构函数重写呢?

struct base//struct默认public
{~base(){cout << "~base\n";}
};struct derived : public base
{~derived(){cout << "~derived\n";}
};int main()
{base* b = new base;delete b;b = new derived;delete b;return 0;
}

上面代码中的父类子类析构函数没有重写

输出结果:

第一行调用了父类的析构函数,没有问题,因为此时b中存的就是一个父类对象

但第二行时b中存的是一个子类对象,这时如果还只调用父类对象的析构函数,子类对象中的动态开辟的内存就会因为没有释放而造成内存泄漏

struct base//struct默认public
{virtual ~base(){cout << "~base\n";}
};struct derived : public base
{virtual ~derived(){cout << "~derived\n";}
};int main()
{base* b = new base;delete b;b = new derived;delete b;return 0;
}

输出结果: 

 

构成重写时,就可以通过指向的对象来调用它的析构函数了(子类对象析构时先析构子类再析构父类)

练习:

下面程序会输出什么?

class A
{
public:virtual void func(int val = 1){cout <<"A->"<<val<<endl;}virtual void test(){func();}
};class B : public A
{
public:virtual void func(int val = 0){cout <<"B->"<<val<<endl;}
};int main()
{B *p = new B;p->test();return 0;
}

输出结果:

 讲解:

test虽然是虚函数,但子类中没有与之对应的test,所以没有重写,那么p还是会去调用A的test

此时要哪个func呢?test的this指针是指向p的,p是B类型对象,所以func就会被编译器写成this->func(),那么就会去调用B的func()

那按上面的流程来讲,因为B.func()的val缺省值是0,应该输出的是B->0啊,为什么输出的是1?

 虚函数的重写是由父类虚函数去覆盖子类虚函数,父类虚函数会把除了函数实现之外的部分都覆盖到子类虚函数,所以父类虚函数中val缺省值会覆盖到子类虚函数中,那此时子类的虚函数中val的缺省值也就变成1了

final和override关键字

被final修饰的虚函数,就代表它不能再被重写了,如果尝试重写它,就会报错

class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒适" << endl;}
};

 被override修饰的虚函数,会检查该虚函数是否重写了父类的某个虚函数,如果没有,就会报错

class Car
{
public:virtual void Dirve(){}
};
class Benz :public Car
{
public:virtual void Drive() override{cout << "Benz-舒适" << endl;}
};

 抽象类

在虚函数的后面直接写上 = 0,则这个函数称为纯虚函数,包含纯虚函数的类就叫做抽象类

class base//抽象类
{
public:virtual void test() = 0;//纯虚函数
};

需要注意的是,抽象类是不能被实例化的

并且即使子类继承了这个抽象类后,也不能被实例化

因为此时derived也包含这个纯虚函数,那么derived也是抽象类

只有在子类中对抽象类的纯虚函数进行重写后,才可以实例化子类对象

class base
{
public:virtual void test() = 0;
};class derived : public base
{
public:virtual void test(){cout << "Hello, world!\n";}
};

此时就可以实例化derived对象了

纯虚函数可以强制子类去进行重写,也表示一个抽象的类型,例如抽象类是花,子类可以是具体的花种

接口继承和实现继承

普通函数的继承被叫做实现继承,而虚函数的继承被叫做接口继承

 因为普通函数的继承是把整个函数继承到子类中,包括函数的具体实现

但虚函数的继承只会把函数名、参数、返回值、缺省值继承下来,函数的具体实现不会被继承,只继承了一个接口,因此被叫作接口继承

虚函数重写的原理:

每个具有虚函数的类都有一个虚函数表,它是一个函数指针数组,每个指针都指向类中的一个虚函数。当一个类对象被实例化时,该对象的前4/8个字节就是指向本类的虚函数表的指针——虚函数表指针(vftptr)

子类对象的虚函数表中就是被子类虚函数覆盖过的,父类对象虚函数表中就是没有被子类覆盖过的,因此,只要去找该对象的虚函数表中对应函数,就可以实现多态。

struct base
{virtual void fun1(){cout << "base:fun1\n";}virtual void fun2(){cout << "base:fun2\n";}int _b;
};struct derived : public base
{virtual void fun1(){cout << "derived:fun1\n";}virtual void fun3(){cout << "derived:fun3\n";}int _s;
};int main()
{base b;derived d;return 0;
}

 可以看到,父类中的虚函数表中有两个指针,分别是父类的fun1和fun2 

而子类的虚函数表中fun1就变成子类的了,又因为fun2没有被覆盖,所以还是父类的

那fun3呢?

这是因为编译器认为fun3不需要显示出来,就刻意的隐藏了。

怎么证明呢?

我们可以打印一下每个类的虚函数表来一探究竟

虚函数表在每个类实例化对象的最上方,如果是x86环境,就是前4个字节,如果是x64环境,就是前8个字节,只需要把前4/8个字节提出来就可以了

打印虚函数表:

首先,需要写个函数,用来打印虚函数表

typedef void(*VF_PTR)();//虚函数表是函数指针数组
void PrintVFTable(VF_PTR pTable[])
{int i = 0;while (pTable[i] != 0)//虚函数表的结束符是在最后填入0x00000000{printf("vfTable[%d]:%p->", i,pTable[i]);(pTable[i])();//调用该函数i++;}
}

那传参时我们怎么传参呢? 

这里以x86环境为例,取出对象的地址后先强转成int*类型,这样就把前4个字节提取出来了,但现在是虚函数表指针的地址,所以需要再解引用这个指针,即*(int*)&b,找到虚函数表指针所指向的虚函数表

然而这时还是int类型,所以要再给它强转成函数指针数组,即(VF_PTR*)*(int*)&d

int main()
{base b;derived d;PrintVFTable((VF_PTR*)*(int*)&b);cout << endl;PrintVFTable((VF_PTR*)*(int*)&d);return 0;
}

这里有些同学可能会有疑问:转换成int*可以,那double*、float*、long*可不可以?

先说答案:long*可以,其他的不行

double*是最不可能的,因为它解引用后是8字节,而我们只要4字节

float*虽然解引用后是4字节,但它是浮点数,float*在内存中不会被识别成指针,而是IEEE 754浮点数,这会导致解引用错误

long*可以,一是因为它也存的是整数,可以被解释为指针类型,二是因为long被解引用后也是4字节

输出结果:

可以看到,在子类的虚函数表中,的确是有fun3的 

虚函数表在哪?

众所周知,类中的函数都是存在内存的代码段的,并且虚函数也是。

那虚函数表存在哪里呢?

很多人可能会认为在栈区,但如果在栈的话,每创建一个对象都要创建一个虚函数表

下面代码中,创建了两个父类对象两个子类对象 

int main()
{base b1;base b2;derived d1;derived d2;return 0;
}

 四个对象的虚函数表指针所存的地址如图所示:

可以看到,同类对象中的虚函数表指针所指向的虚函数表都是同一个

所以,虚函数表不是在栈区。又因为堆区是给动态开辟内存用的,静态区是给静态变量和全局变量,所以只能是代码段了。即虚函数表存在代码段中。

怎么证明这点呢?

可以写一个程序,来分别打印各个地址区变量/常量的地址

struct base
{virtual void fun1(){cout << "base:fun1\n";}virtual void fun2(){cout << "base:fun2\n";}void test() { cout << "这是一个函数\n"; }int _b;
};struct derived : public base
{virtual void fun1(){cout << "derived:fun1\n";}virtual void fun3(){cout << "derived:fun3\n";}int _s;
};int qi;//全局变量也在数据段
int main()
{base b1;int i,j;//栈区变量int* p1 = new int;//堆区变量int* p2 = new int;static int si;//静态变量也在数据段char* st = "野兽先辈";//常量存在代码段printf("VFTable(虚函数表)地址:%p\n", *(int*) & b1);printf("栈区变量i地址:%p\n", &i);printf("栈区变量j地址:%p\n", &j);printf("堆区变量p1地址:%p\n", p1);printf("堆区变量p2地址:%p\n", p2);printf("数据段变量qi地址:%p\n", &qi);printf("数据段变量si地址:%p\n", &si);printf("代码段常量st地址:%p\n", st);printf("代码段普通函数地址:%p\n", &base::test);printf("代码段虚函数地址:%p\n", &base::fun1);return 0;
}

在输出结果中,可以看到,虚函数表(简称虚表)的地址和代码段的地址是最接近的

因此,虚函数表是存储在代码段中

动态绑定与静态绑定

动态绑定就是普遍意义上的多态,即在程序运行期间,根据具体拿到的类型确定程序的具体
行为,调用具体的函数。

静态绑定是在程序编译期间确定了程序的行为

动态绑定又称为动态多态,静态绑定又称为静态多态。

一般说的多态就是动态多态,只是有些题里可能会分开讲

 多继承中的虚函数表

上面讲过单继承中的虚函数表,下面来讲讲多继承中的虚函数表

在单继承中,虚函数表都是被放在父类的最开始处,在多继承也是,只不过每个父类的最开始处都有一个虚函数表

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;
};
typedef void(*VFPTR) ();//虚函数表是函数指针数组
void PrintVTable(VFPTR VFTable[])
{for (int i = 0; VFTable[i] != 0; ++i){printf(" VFTable[%d]:%p,->", i, VFTable[i]);VFTable[i]();}cout << endl;
}
int main()
{Derive d;PrintVTable((VFPTR*)(*(int*)&d));//打印第一个虚函数表(也就是Base1的)PrintVTable((VFPTR*)(*(int*)((char*)&d + sizeof(Base1))));//打印第二个虚函数表(也就是Base2的)//这里是先将地址+"Base1"个字节数,这样就能到Base2的空间return 0;
}

这段程序中,Derive类继承了两个父类,并且子类中有一个虚函数没有被重写,两个父类中的fun2也没有被重写

PrintVTable用于打印该类的虚函数表

对于PrintVTable((VFPTR*)(*(int*)((char*)&d + sizeof(Base1))));这里,是要取到第二个继承的父类的虚函数表,那就在第一个父类的后面,所以要让子类地址+父类字节数。但如果直接加的话,内存中移动是以d的类型为单位,也就是Derived,所以我们需要让它变为以1为一个单位,就要转换为char*

输出结果:

 可以看到,两个父类中的fun1都被子类重写,fun2都保持不变

但子类中没有被重写的fun3,却到了Base1的虚函数表中了

即:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中

相关文章:

【语法】C++的多态

目录 虚函数的重写&#xff1a; 虚函数 重写(覆盖) 虚函数重写的两个例外&#xff1a; 协变&#xff1a; 析构函数的重写&#xff1a; 练习&#xff1a; final和override关键字 抽象类 接口继承和实现继承 虚函数重写的原理&#xff1a; 打印虚函数表&#xff1a; …...

WebGIS开发新突破:揭秘未来地理信息系统的神秘面纱

你有没有想过&#xff0c;未来的地理信息系统&#xff08;GIS&#xff09;会是什么样子&#xff1f;是像电影里那样&#xff0c;一块透明屏幕就能呈现整个城市的实时动态&#xff1f;还是像《钢铁侠》中那样&#xff0c;一个手势就能操控全球地图&#xff1f; 其实&#xff0c…...

JVM类加载

JVM类加载 1. 类的生命周期&#xff08;类加载过程&#xff09;类加载的五个阶段&#xff1a; 2. 类加载器的分类3. 双亲委派模型4. 类的卸载与热加载5.类加载器命名空间隔离 1. 类的生命周期&#xff08;类加载过程&#xff09; 类加载的五个阶段&#xff1a; 加载&#xff…...

AD开启交叉选择功能,只选中器件,不选中网络、焊盘

AD开启交叉选择功能&#xff0c;只选中器件&#xff0c;不选中网络、焊盘。 一、打开首选项 二、打开System→Navigationg&#xff0c;配置如下。 三、最后点击OK即可。...

机器学习——集成学习基础

一、鸢尾花数据训练模型 1. 使用鸢尾花数据分别训练集成模型&#xff1a;AdaBoost模型&#xff0c;Gradient Boosting模型 2. 对别两个集成模型的准确率以及报告 3. 两个模型的预测结果进行可视化 需要进行降维处理&#xff0c;两个图像显示在同一个坐标系中 代码展示&…...

C++匿名函数

C 中的匿名函数&#xff08;Lambda 表达式&#xff09;是 C11 引入的一项重要特性&#xff0c;它允许你在需要的地方定义一个临时的、无名的函数对象&#xff0c;使代码更加简洁和灵活。 1. 基本语法 Lambda 表达式的基本结构&#xff1a; [capture list](parameter list) -…...

互联网大厂Java面试实战:Spring Boot到微服务的技术问答解析

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…...

神经网络是如何工作的

人工智能最核心的技术之一&#xff0c;就是神经网络&#xff08;Neural Networks&#xff09;。但很多初学者会觉得它是个黑盒&#xff1a;为什么神经网络能识别图片、翻译语言&#xff0c;甚至生成文章&#xff1f; 本文用图解最小代码实现的方式&#xff0c;带你深入理解&am…...

Kubernetes控制平面组件:Kubelet详解(二):核心功能层

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

【android bluetooth 框架分析 02】【Module详解 13】【CounterMetrics 模块介绍】

1. CounterMetrics 介绍 CounterMetrics 模块代码很少&#xff0c; 我简单介绍一下。 // system/gd/metrics/counter_metrics.cc #define LOG_TAG "BluetoothCounterMetrics"#include "metrics/counter_metrics.h"#include "common/bind.h" #i…...

Matlab自学笔记五十四:符号数学工具箱和符号运算、符号求解、绘图

1.什么是符号数学工具箱&#xff1f; 符号数学工具箱是Matlab针对符号对象的运算功能&#xff0c;它引入了一种特殊的数据类型 - 符号对象&#xff1b; 该数据类型包括符号数字&#xff0c;符号变量&#xff0c;符号表达式和符号函数&#xff0c;还包含符号矩阵&#xff0c;以…...

Matlab 模糊控制平行侧边自动泊车

1、内容简介 Matlab 233-模糊控制平行侧边自动泊车 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...

新书速览|纯血鸿蒙HarmonyOS NEXT原生开发之旅

《纯血鸿蒙HarmonyOS NEXT原生开发之旅》 本书内容 《纯血鸿蒙HarmonyOS NEXT原生开发之旅》全面系统地介绍了基于HarmonyOS NEXT系统进行原生应用开发的实用技巧。全书共12章&#xff0c;内容涵盖从基础工具使用到高级功能实现的各个方面。第1章详细介绍了开发环境的搭建、Ar…...

tinyint(3)数据类型讲解

TINYINT(3) 是数据库中用于定义字段数据类型的一种写法&#xff0c;常见于 MySQL 等数据库系统。下面来详细了解其含义和作用&#xff1a; 数据类型本质 TINYINT 属于整数类型&#xff0c;在不同的数据库系统中&#xff0c;它所占用的存储空间和表示范围通常是固定的。以 MyS…...

manjaro系统详解

1. Manjaro 概述 Manjaro 是一款基于 Arch Linux 的滚动更新发行版&#xff0c;以 用户友好、易用性 和 硬件兼容性 为核心设计理念。它继承了 Arch 的灵活性和软件丰富性&#xff0c;同时通过图形化工具和稳定的更新策略降低了使用门槛&#xff0c;适合从新手到高级用户的广泛…...

# 实时英文 OCR 文字识别:从摄像头到 PyQt5 界面的实现

实时英文 OCR 文字识别&#xff1a;从摄像头到 PyQt5 界面的实现 引言 在数字化时代&#xff0c;文字识别技术&#xff08;OCR&#xff09;在众多领域中发挥着重要作用。无论是文档扫描、车牌识别还是实时视频流中的文字提取&#xff0c;OCR 技术都能提供高效且准确的解决方案…...

9.3.云原生架构模式

目录 一、云原生架构核心概念 云原生定义与核心原则 • 四大核心要素&#xff1a;容器化、微服务、DevOps、持续交付 • 核心原则&#xff1a;弹性、可观测性、自动化、不可变基础设施 云原生技术矩阵 • 容器与编排&#xff1a;Docker、Kubernetes、CRI-O • 服务治理&#…...

现代化水库运行管理矩阵平台如何建设?

政策背景 2023年8月24日&#xff0c;水利部发布的水利部关于加快构建现代化水库运行管理矩阵的指导意见中指出&#xff0c;在全面推进水库工程标准化管理的基础上&#xff0c;强化数字赋能&#xff0c;加快构建以推进全覆盖、全要素、全天候、全周期“四全”管理&#xff0c;完…...

木马查杀引擎—关键流程图

记录下近日研究的木马查杀引擎&#xff0c;将关键的实现流程图画下来 PHP AST通道实现 木马查杀调用逻辑 模型训练流程...

基于libevent的异步事件驱动型线程池实现

----------------------| IFoxThread | ← 抽象线程接口|----------------------|| dispatch() || start() || stop() || ... |----------^-----------|--------------------|----------------------| …...

ArcGIS+InVEST+RUSLE:水土流失模拟与流域管理的高效解决方案;水土保持专题地图制作

在全球生态与环境面临严峻挑战的当下&#xff0c;水土流失问题已然成为制约可持续发展的重要因素之一。水土流失不仅影响土地资源的可持续利用&#xff0c;还对生态环境、农业生产以及区域经济发展带来深远影响。因此&#xff0c;科学、精准地模拟与评估水土流失状况&#xff0…...

#S4U2SELF#S4U2Proxy#CVE-2021-42278/42287

#S4U2SELF Win08创建普通用户 s4u2 xwj456 可以看到普通用户是没用委托属性的 Win08手动赋予委托服务属性 setspn -A wsw/wsw.com s4u2 Win10身份验证 s4u2 xwj456 AS请求 两个勾 两个勾和include-pac记得按上&#xff08;蓝色&#xff09; &#xff0c;发包之前把wiresh…...

利用基于LLM的概念提取和FakeCTI数据集提升网络威胁情报对抗虚假信息活动的能力

摘要 虚假新闻和虚假信息宣传活动的迅速蔓延对公众信任、政治稳定和网络安全构成了重大威胁。传统的网络威胁情报&#xff08;CTI&#xff09;方法依赖于域名和社交媒体账号等低级指标&#xff0c;很容易被频繁修改其在线基础设施的对手规避。为了解决这些局限性&#xff0c;我…...

uniapp|实现手机通讯录、首字母快捷导航功能、多端兼容(H5、微信小程序、APP)

基于uniapp实现带首字母快捷导航的通讯录功能,通过拼音转换库实现汉字姓名首字母提取与分类,结合uniapp的scroll-view组件与pageScrollTo API完成滚动定位交互,并引入uni-indexed-list插件优化索引栏性能。 目录 核心功能实现动态索引栏生成​联系人列表渲染​滚动定位联动性…...

使用PhpStudy搭建Web测试服务器

一、安装PhpStudy 从以下目录下载PhpStudy安装文件 Windows版phpstudy下载 - 小皮面板(phpstudy) (xp.cn) 安装成功之后打开如下界面 点击启动Apache 查看网站地址 在浏览器中输入localhost:88,出现如下页面就ok了 二、与Unity交互 1.配置下载文件路径&#xff0c;点击…...

Qt/C++面试【速通笔记九】—视图框架机制

在Qt中&#xff0c;QGraphicsView和QGraphicsScene是用于构建二维图形界面的核心组件。它们的设计使得开发者能够高效地管理和渲染图形项&#xff0c;支持丰富的用户交互&#xff0c;例如缩放、旋转、平移等。 1. QGraphicsScene和QGraphicsView的基本概念 QGraphicsScene QG…...

react-diff-viewer 如何实现语法高亮

前言 react-diff-viewer 是一个很好的 diff 展示库&#xff0c;但是也有一些坑点和不完善的地方&#xff0c;本文旨在描述如何在这个库中实现自定义语法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…...

Python实例题:Django搭建简易博客

目录 Python实例题 题目 1. 创建 Django 项目和应用 2. 配置项目 3. 设计模型 blog_app templates blog_app post_list.html admin.py models.py urls.py views.py blog_project urls.py 代码解释 models.py&#xff1a; admin.py&#xff1a; urls.py&…...

Kotlin 异步初始化值

在一个类初始化的时候或者方法执行的时候&#xff0c;总有一些值是需要的但是不是立即需要的&#xff0c;并且在需要的时候需要阻塞流程来等待值的计算&#xff0c;这时候异步的形式创建这个值是毋庸置疑最好的选择。 为了更好的创建值需要使用 Kotlin 的协程来创建&#xff0…...

扩展:React 项目执行 yarn eject 后的 config 目录结构详解

扩展&#xff1a;React 项目执行 yarn eject 后的 config 目录结构详解 什么是 yarn eject&#xff1f;React 项目执行 yarn eject 后的 config 目录结构详解&#x1f4c1; config 目录结构各文件作用详解env.jsgetHttpsConfig.jsmodules.jspaths.jswebpack.config.jswebpackDe…...

(自用)Java学习-5.8(总结,springboot)

一、MySQL 数据库 表关系 一对一、一对多、多对多关系设计外键约束与级联操作 DML 操作 INSERT INTO table VALUES(...) DELETE FROM table WHERE... UPDATE table SET colval WHERE...DQL 查询 基础查询&#xff1a;SELECT * FROM table WHERE...聚合函数&#xff1a;COUNT()…...

cursor 如何在项目内自动创建规则

在对话框内 / Generate。cursor rules 就会自动根据项目进行创建规则 文档来自&#xff1a;https://www.kdocs.cn/l/cp5GpLHAWc0p...

C++ 迭代器

1.用途&#xff1a; 像我们之前学习的容器map,vector等&#xff0c;如果需要遍历该怎么做呢&#xff1f;这些容器大部分对下标式遍历&#xff0c;无法像数组灵活使用&#xff0c;也包括增删改查&#xff0c;因为它们的特性&#xff0c;所以需要一种其他的方法。 那么迭代器就…...

基于微信小程序的城市特色旅游推荐应用的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...

最大m子段和

问题描述解题思路伪代码代码实现复杂度分析 问题描述 给定一个有n(n>0)个整数的序列&#xff0c;要求其m个互不相交的子段&#xff0c;使得这m个子段和最大。 输入&#xff1a;整数序列{nums}&#xff0c;m。 输出&#xff1a;最大m子段和。 对于m1的情况&#xff0c;即求最…...

4.MySQL全量、增量备份与恢复

1.数据备份的重要性 在企业中数据的价值至关重要&#xff0c;数据保障了企业业务的正常运行。因此&#xff0c;数据的安全性及数据的可靠性是运维的重中之重&#xff0c;任何数据的丢失都可能对企业产生严重的后果。通常情况下造成数据丢失的原因有如下几种&#xff1a; a.程…...

每日算法刷题Day4 5.12:leetcode数组4道题,用时1h

7. 704.二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 思想 二分模版题 代码 c: class Solution { public:int search(vector<int>& nums, int target) {int nnums.size();int left0,rightn-1;int res-1;while(left<right){int midleft((…...

Day 15

目录 1.chika和蜜柑1.1 解析1.2 代码 2.对称之美2.1 解析2.2 代码 3.添加字符3.1 解析3.2 代码 1.chika和蜜柑 chika和蜜柑 TopK、堆、排序 1.1 解析 1.2 代码 #include <iostream> #include <vector> #include <algorithm> using namespace std; struct …...

脑机接口重点产品发展路径分析:以四川省脑机接口及人机交互产业攻坚突破行动计划(2025-2030年)为例

引言 随着人工智能和生物技术的飞速发展,脑机接口技术作为连接人类大脑与智能设备的桥梁,正在成为全球科技竞争的新焦点。2025年5月12日,四川省经济和信息化厅等8部门联合印发了《四川省脑机接口及人机交互产业攻坚突破行动计划(2025-2030年)》,为四川省在这一前沿领域的…...

leetcode 18. 四数之和

题目描述 和leetcode 15. 三数之和用同样的方法。有两个注意点。 一是剪枝的逻辑 这是和15. 三数之和 - 力扣&#xff08;LeetCode&#xff09;问题不同的地方。 无法通过这种情况&#xff1a; 二是整数溢出 最终答案 class Solution { public:vector<vector<int>…...

CentOS部署Collabora Online

1.安装Docker CentOS7安装Docker(超详细)-CSDN博客 2.拉取镜像 docker pull collabora/code:latest 3. 启动容器&#xff08;直接暴露HTTP端口&#xff09; docker run -d --name collabora -p 9980:9980 -e "usernameadmin" -e "password123456" -e …...

《Spring Boot 4.0新特性深度解析》

Spring Boot 4.0的发布标志着Java生态向云原生与开发效能革命的全面迈进。作为企业级应用开发的事实标准框架&#xff0c;此次升级在运行时性能、云原生支持、开发者体验及生态兼容性四大维度实现突破性创新。本文深度解析其核心技术特性&#xff0c;涵盖GraalVM原生镜像支持、…...

FFmpeg 与 C++ 构建音视频处理全链路实战(一)—— 环境配置与视频解封装

在数字媒体的浩瀚宇宙中&#xff0c;FFmpeg 就像一艘功能强大的星际战舰&#xff0c;承载着处理音视频数据的重任。而 C 作为一门高效、灵活的编程语言&#xff0c;犹如一位技艺精湛的星际工程师&#xff0c;能够精准操控 FFmpeg 战舰&#xff0c;完成各类复杂的音视频处理任务…...

什么是 NoSQL 数据库?它与关系型数据库 (RDBMS) 的主要区别是什么?

我们来详细分析一下 NoSQL 数据库与关系型数据库 (RDBMS) 的主要区别。 什么是 NoSQL 数据库&#xff1f; NoSQL (通常指 “Not Only SQL” 而不仅仅是 “No SQL”) 是一类数据库管理系统的总称。它们的设计目标是解决传统关系型数据库 (RDBMS) 在某些场景下的局限性&#xf…...

AI需求分析话术 | DeepSeek R1

运行环境&#xff1a;jupyter notebook (python 3.12.7) Dash 场景&#xff1a; 收集了小程序的问题点和优化建议&#xff0c;一键AI分析&#xff0c;快速排优先级 指令话术&#xff1a; 对收集的小程序问题点和建议&#xff0c;做需求分析并总结形成报告&#xff0c;报告结构…...

【Redis】键值对数据库实现

目录 1、背景2、五种基本数据类型对应底层实现3、redis数据结构 1、背景 redis是一个&#xff08;key-value&#xff09;键值对数据库&#xff0c;其中value可以是五大基本数据类型&#xff1a;string、list、hash、set、zset&#xff0c;这五大基本数据类型对应着不同的底层结…...

MySQL 8.0 OCP 英文题库解析(三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题16~25 试题16:…...

互联网大厂Java求职面试:优惠券服务架构设计与AI增强实践-1

互联网大厂Java求职面试&#xff1a;优惠券服务架构设计与AI增强实践-1 在一间简洁明亮的会议室里&#xff0c;郑薪苦正面对着一位技术总监级别的面试官&#xff0c;这位面试官拥有超过十年的大型互联网企业经验&#xff0c;以技术全面性与落地能力著称。 第一轮面试&#xf…...

object的常用方法

在面向对象编程中&#xff0c;Object 类是所有类的根类&#xff0c;它提供了一些基本的方法&#xff0c;这些方法可以被所有对象继承和使用。以下是一些在 Java 中 Object 类的常用方法&#xff0c;以及它们的作用和使用示例&#xff1a; 1. equals(Object obj) 作用&#xff…...

解决vue create 创建项目,不能使用上下键选择模板的问题

使用 git bash 创建vue项目时候&#xff0c;无法使用上下键盘按键选择创建模板 处理&#xff1a; 1.当前界面&#xff0c;按CTR C终止创建命令&#xff1b; 2.使用 alias vuewinpty vue.cmd&#xff0c;更新命令环境&#xff1b; 3.再次使用 vue create demo创建项目&#xf…...