C++ —— 多态
目录
1.多态的概念
2.多态的定义及实现
2.1构成多态的两个硬性条件
2.2虚函数的重写
2.3override和final
3.抽象类
3.1接口继承和实现继承
4.多态原理
4.1虚函数表
4.2原理
4.3静态绑定和动态绑定
5.单继承和多继承体系的虚函数表
5.1单继承体系的虚函数表
5.2多继承体系的虚函数表
6.继承和多态常见面试问题
6.1概念考察
6.2问答题
1.多态的概念
多态的前提的是继承。当不同的对象去完成同一种行为时会产生不同的结果就是多态的通俗意义。
例如学生、成人两个对象去完成买票这个行为,那么学生的结果是获得半价,而成人获得的结果的是全价。
2.多态的定义及实现
2.1构成多态的两个硬性条件
调用的函数必须是虚函数,且派生类必须对虚函数重写;必须是基类的引用或指针调用虚函数。
虚函数就是在成员函数之前加一virtual关键字。虚函数的目的就是为了实现多态。
下面的程序是一个多态的案例:
class Person
{
public:virtual void print(){cout << "Person::全价" << endl;}
};class Student : public Person
{
public:virtual void print(){cout << "Student::半价" << endl;}
};void test(Person* ptr) //传入的对象不同调用不同的虚函数
{ptr->print(); //必须是基类的指针或引用调用虚函数
}
int main()
{Student s;Person p;test(&p);test(&s);return 0;
}

2.2虚函数的重写
派生类中有一个与基类完全相同的虚函数(返回值、函数名、参数),但是定义是派生类想要的,这样的,称派生类完成了对基类虚函数的重写(覆盖)。
派生类的重写虚函数不一定非要加virtual关键字,因为基类被继承之后其虚函数依旧保持其原有的虚函数属性,但是这样的代码风格是不规范的。
class Student : public Person
{
public:void print() //不加virtual关键字也可以{cout << "Student::半价" << endl;}
};
如果派生类没有对虚函数进行重写,那么派生类依然有基类原有的虚函数。
class Student : public Person
{//派生类没有重写基类的虚函数//那么派生类依然持有基类原有的虚函数
};
虚函数重写的两个例外:
1.协变(基类虚函数和派生类虚函数的返回值不同)
基类虚函数可以返回任意具有继承关系的基类的指针或引用;派生类虚函数可以返回任意具有继承关系的派生类的指针或引用;前两者的返回值必须是同一个继承系统。
class A
{};
class B : public A
{};// 基类虚函数可以返回任意具有继承关系的基类的指针或引用
class Person
{
public:virtual A* print(){cout << "Person::全价" << endl;return nullptr;}
};// 派生类虚函数可以返回任意具有继承关系的派生类的指针或引用
class Student : public Person
{
public:virtual B* print(){cout << "Student::半价" << endl;return nullptr;}
};
2.派生类的析构函数与基类的析构函数构成重写关系
如果基类的析构函数是虚函数,那么派生类的析构函数无论是否带有virtual关键字都与基类的析构函数构成重写。其原因在于:在多态系统中,编译器会自动将基类和派生类的析构函数解析为同名的destructor()函数。必须让派生类的析构函数与基类的析构函数构成重写关系的原因如下段代码所示:
class A
{
public:virtual ~A(){cout << "A::~A()" << endl;}
};class B : public A
{
public:~B(){cout << "B:~B()" << endl;}
};int main()
{// 如果基类和派生类的析构函数不构成多态系统// 那么在清理资源时就会产生重复释放A* pa = new A;A* pb = new B;delete pa;delete pb;return 0;
}
2.3override和final
C++对重写的要求比较严格,我们可以借助override和final这两个关键字检测派生类是否完成对基类虚函数的重写。
1.final放在类名后面,表示此类不能被继承:
class A final
{};class B : public A //错误,A类不能被继承
{};
2.final修饰虚函数,表明该虚函数不能被重写:
class Car {
public:virtual void Drive () final{}
};class Benz :public Car {
public:virtual void Drive() //错误,此虚函数不能被重写{ cout << "Benz-舒适" << endl; }
};
3.override可以检查派生类虚函数是否完成对基类虚函数的重写:
class Car {
public:virtual void Drive () const{}
};class Benz :public Car {
public:// 报错,多余Drive函数,派生类没有正确重写virtual void Drive() override{}
};
3.抽象类
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口
类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生
类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
class Car
{
public:virtual void Drive() = 0;
};class Benz :public Car
{
public:
};int main()
{Benz b; //错误,Benz类没有完成基类纯虚函数的重写return 0;
}
3.1接口继承和实现继承
普通成员函数的继承是一种实现继承,派生类继承了基类普通成员函数的所有东西(返回值、函数名、参数列表、函数定义)。虚函数的继承是一种接口继承,派生类只继承了基类虚函数的接口(返回值、函数名、参数列表),其目的就是为了派生类能够对虚函数进行重写(即使没有对基类虚函数进行重写,派生类依然保持基类原有虚函数,这是特性)。
下面是一道练习题:
// 下面程序输出结果是什么?
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指向派生类对象,那么这是一次普通函数调用。调用的函数是基类继承到派生类的虚函数,但是派生类没有对此函数完成重写,所以调用func函数时的this指针是基类指针,是一次多态调用。这个基类指针指向的是派生类对象(外部new了一个派生类对象),所以调用的是派生类重写之后的func函数,又因为接口继承,所以派生类的func函数的参数的缺省值为1,所以最终结果为B->1。

4.多态原理
4.1虚函数表
我们使用一段代码来引用出虚函数表:
// 32位平台下,sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};int main()
{cout << sizeof(Base) << endl;return 0;
}
解析:如果没有Func1这个虚函数,那么答案绝对是4。但是这里的输出为8。其原因在于,类中的虚函数需要放到虚函数表中,一个含有虚函数的类至少有一个虚函数表的指针,虚函数表也称虚表。所以一个含有虚函数的类,不仅仅存放了成员变量,还存放了一个虚表指针。

我们使用更为复杂的代码往下分析:
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.派生类对象由两部分构成,一部分是自己的成员,另一部分是基类成员;派生类对象也有也有一个虚表指针,这个指针存放在基类部分当中。
2.基类对象和派生类对象虚表是不一样的,这里我们发现派生类完成了Func1的重写,所以派生类对象的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖。覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
3.非虚函数的函数地址不会放入虚表中。
4.虚函数表本质是一个存放虚函数指针的指针数组(函数指针数组),一般情况这个数组以nullptr结尾(VS环境下,Linux可能不一样)。
5.派生类的虚函数表是怎样生成的?先将基类虚表内容拷贝一份至派生类的虚表当中(因为这两个虚表的地址不一样,所以可以断定派生类的虚表是自己生成的)。如果派生类完成了对某个虚函数的重写,就会将重写的虚函数的地址放入虚表的对应位置。派生类自己新增的虚函数会依照声明顺序增加到派生类虚表的后面。
4.2原理
class Person
{
public:virtual void print(){cout << "Person::全价" << endl;}
};class Student : public Person
{
public:virtual void print(){cout << "Student::半价" << endl;}
};void test(Person& ptr) //传入的对象不同调用不同的虚函数
{ptr.print(); //必须是基类的指针或引用调用虚函数
}
int main()
{Student s;Person p;test(p);test(s);return 0;
}
为什么根据传入对象的不同就能调用不同的虚函数?无论基类的指针或引用绑定的对象是基类对象还是派生类对象,他们都是没有区别的(绑定到派生类对象时会发生切片动作),所以在编译的时候无法确定基类的指针或引用到底绑定的是基类对象还是派生类对象中的基类部分,但是这是正确的,所以编译会通过。当需要调用虚函数时,根本无法确定调用的是哪个虚函数,所以只能在程序运行时去虚函数表当中找被调用虚函数的地址。此时无论基类指针或引用绑定的对象是谁,只需要根据虚表内容不同,调用不同的虚函数即可。

4.3静态绑定和动态绑定
1.静态绑定:静态绑定在程序编译阶段就确定了行为,也称静态多态,例如函数重载。
2.动态绑定:动态绑定在编译阶段无法确定行为,只能在程序运行期间根据具体类型调用具体的函数,也称动态多态。
5.单继承和多继承体系的虚函数表
5.1单继承体系的虚函数表
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int b;
};int main()
{Derive d;return 0;
}
观察监视窗口发现:派生类的虚函数表中并没有func3函数的地址。是不是前面的理论是错误的?

其实不然,我们可以将虚表内容打印出来:
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a = 1;
};class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int b = 2;
};typedef void (*VFPTR)();void print_VFtable(VFPTR vf[])
{cout << "虚表地址:0x" << vf << endl;for (int i = 0; vf[i] != nullptr; i++){printf("[%d]:0x%p->", i, vf[i]);vf[i]();}cout << endl;
}
int main()
{Base b;Derive d;// 对象的前4/8个字节存放的是虚表指针VFPTR* vfb = (VFPTR*)(*(int*)&b);print_VFtable(vfb);VFPTR* vfd = (VFPTR*)(*(int*)&d);print_VFtable(vfd);return 0;
}

结论:派生类新增的虚函数的地址会放入派生类的虚表中,但是监视窗口不能体现出来。
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;
};typedef void (*VFPTR)();void print_VFtable(VFPTR vf[])
{cout << "虚表地址:0x" << vf << endl;for (int i = 0; vf[i] != nullptr; i++){printf("[%d]:0x%p->", i, vf[i]);vf[i]();}cout << endl;
}
int main()
{Base1 b1;Base2 b2;Derive d;// 对象的前4/8个字节存放的是虚表指针VFPTR* vfb1 = (VFPTR*)(*(void**)&b1);print_VFtable(vfb1); //打印b1对象的虚表VFPTR* vfb2 = (VFPTR*)(*(void**)&b2);print_VFtable(vfb2); //打印b2对象的虚表VFPTR* vfd1 = (VFPTR*)(*(void**)&d);print_VFtable(vfd1); //打印d对象的第一张虚表VFPTR* vfd2 = (VFPTR*)(*(void**)((char*)&d + sizeof(Base1)));print_VFtable(vfd2); //打印d对象的第二张虚表return 0;
}

结论:派生类重写虚函数时是对多个基类的虚函数重写;派生类自己定义的虚函数的地址存放在派生类的第一张虚表当中。
6.继承和多态常见面试问题
6.1概念考察
1. 下面哪种面向对象的方法可以让你变得富有( )
A: 继承 B: 封装 C: 多态 D: 抽象答案:A。继承可以定义一个与其他类相似的一个新类,并且这两个类保持一定的联系。
2. ( )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象。
A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定
答案:D。多态(动态绑定)可以实现题目描述的效果。
3. 面向对象设计中的继承和组合,下面说法错误的是?()
A:继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复
用,也称为白盒复用
B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动
态复用,也称为黑盒复用
C:优先使用继承,而不是组合,是面向对象设计的第二原则
D:继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封
装性的表现
答案:C。优先应使用组合,因为组合并没有破坏原有类的封装性且耦合度较低。
4. 以下关于纯虚函数的说法,正确的是( )
A:声明纯虚函数的类不能实例化出对象B:声明纯虚函数的类是虚基类
C:派生类必须实现基类的纯虚函数
D:纯虚函数必须是空函数
答案:A。B中的虚基类是虚继承的概念。
5.关于虚函数的描述正确的是()
A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型
B:内联函数不能是虚函数
C:派生类必须重新定义基类的虚函数
D:虚函数可以是一个static型的函数
答案:B。虚函数的调用必须通过虚表,如果是内联函数就破坏了这个行为。D中的说法是错误的,因为static函数没有this指针。
6. 关于虚表说法正确的是( )
A:一个类只能有一张虚表
B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
C:虚表是在运行期间动态生成的
D:一个类的不同对象共享该类的虚表
答案:D。虚表也是存放在代码区的,与成员函数一样。
7. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( )
A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
B:A类对象和B类对象前4个字节存储的都是虚基表的地址
C:A类对象和B类对象前4个字节存储的虚表地址相同
D:A类和B类虚表中虚函数个数相同,但A类和B类使用的不是同一张虚表
答案:D。B中虚基表的说法是错误的。
8. 下面程序输出结果是什么? ()
#include<iostream> using namespace std; class A { public:A(const char* s) { cout << s << endl; }~A() {} }; class B :virtual public A { public:B(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; } }; class C :virtual public A { public:C(const char* s1, const char* s2) :A(s1) { cout << s2 << endl; } }; class D :public B, public C { public:D(const char* s1, const char* s2, const char* s3, const char* s4) :B(s1, s2), C(s1, s3), A(s1){cout << s4 << endl;} }; int main() {D* p = new D("class A", "class B", "class C", "class D");delete p;return 0; }A:class A class B class C class D B:class D class B class C class A
C:class D class C class B class A D:class A class C class B class D答案:A。因为是菱形虚拟继承,所以A类成员是B类和C类共享的,所以B和C两个类其中任意一个去初始化A都不合适(它们两个的构造函数显示调用A的构造函数是因为或许在某个场景下需要单独实例化B或C的对象),所以初始化A的任务只能放给D了。初始化列表的初始化顺序与声明顺序有关,很明显:D类是先继承B再继承C,而在继承B之前B已经继承A了,所以初始化顺序为,A->B->C->D。
9. 多继承中指针偏移问题?下面说法正确的是( )
class Base1 { public: int _b1; }; class Base2 { public: int _b2; }; class Derive : public Base1, public Base2 { public: int _d; }; int main(){ Derive d; Base1* p1 = &d; Base2* p2 = &d; Derive* p3 = &d; return 0; }A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3
答案:C。Derive类对象模型可以是下面这样:
10. 以下程序输出结果是什么()
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: 以上都不正确
答案:B。上面已经讲解过。
6.2问答题
1. 什么是多态?答:参考本篇博客内容。
2. 什么是重载、重写(覆盖)、重定义(隐藏)?答:参考本篇博客内容与上一篇继承博客。
3. 多态的实现原理?答:参考本篇博客内容。
4. 虚函数可以声明为inline函数吗?答:可以,不过编译器会忽略内联属性,因为虚函数不能做内联函数(inline是一个建议选项)。
5. 静态成员可以是虚函数吗?答:不能,因为静态成员函数没有this指针,使用类作用域限定符直接访问函数无法访问虚表(通过指针运算访问),所以静态成员函数不能被放进虚表。
6. 构造函数可以是虚函数吗?答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?答:可以。在继承体系中,外部有动态释放的资源时。
8. 对象访问普通函数快还是虚函数更快?答:如果非多态调用,二者一样快;如果是多态调用,虚函数的访问比普通函数慢,因为调用虚函数需要经过虚表。
9. 虚函数表是在什么阶段生成的,存在哪的?答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。
10. C++菱形继承的问题?虚继承的原理?答:参考继承博客。注意虚基表是虚继承的概念,虚基表存放的是偏移量;虚表是多态的概念,存放的是虚函数的地址。
11. 什么是抽象类?抽象类的作用?答:参考本篇博客内容。
相关文章:
C++ —— 多态
目录 1.多态的概念 2.多态的定义及实现 2.1构成多态的两个硬性条件 2.2虚函数的重写 2.3override和final 3.抽象类 3.1接口继承和实现继承 4.多态原理 4.1虚函数表 4.2原理 4.3静态绑定和动态绑定 5.单继承和多继承体系的虚函数表 5.1单继承体系的虚函数表 5.2多继…...
java agent设计开发概要
agent开发设计 agent 开发的一些心得,适合熟悉agent或者有agent开发需求的同学 1 有个基础的agent,是java 标准的agent。这是agent代码入口 2 设计包结构, 基础agent agent下有plugin,加载plugin可以自己定义一个类加载器 plugin࿱…...
node.js笔记-模块化(commonJS规范),包与npm(Node Package Manager)
目录 模块化 node.js中模块的分类 模块的加载方式 模块作用域 向外共享模块作用域中的成员 向外共享成员 包与npm(Node package Manager) 什么是包? 包的来源 为什么需要包? 查找和下载包 npm下载和卸载包命令 配置np…...
Linux 磁盘坏块修复处理(错误:read error: Input/output error)
当磁盘出现坏块时,你对所关联的文件进行读取时,一般会出现 read error: Input/output error 这样的错误。 反过来讲,当你看到 read error: Input/output error 这种错误时,很大可能就是磁盘出现了坏块问题。 解决步骤:…...
API 面试四连杀:接口如何设计?安全如何保证?签名如何实现?防重如何实现?
下面我们就来讨论下常用的一些API设计的安全方法,可能不一定是最好的,有更牛逼的实现方式,但是这篇是我自己的经验分享. 一、token 简介 Token:访问令牌access token, 用于接口中, 用于标识接口调用者的身份、凭证,减…...
操作系统题目收录(六)
1、某系统采用基于优先权的非抢占式进程调度策略,完成一次进程调度和进程切换的系统时间开销为1us。在T时刻就绪队列中有3个进程P1P_1P1、P2P_2P2和P3P_3P3,其在就绪队列中的等待时间、需要的CPU时间和优先权如下表所示。若优先权值大的进程优先获…...
2023年十款开源测试开发工具推荐!
今天为大家奉献一篇测试开发工具集锦干货。在本篇文章中,将给大家推荐10款日常工作中经常用到的测试开发工具神器,涵盖了自动化测试、性能压测、流量复制、混沌测试、造数据等。 1、AutoMeter-API 自动化测试平台 AutoMeter 是一款针对分布式服务&…...
MySQL慢查询分析和性能优化
1 背景我们的业务服务随着功能规模扩大,用户量扩增,流量的不断的增长,经常会遇到一个问题,就是数据存储服务响应变慢。导致数据库服务变慢的诱因很多,而RD最重要的工作之一就是找到问题并解决问题。下面以MySQL为例子&…...
C++学习笔记(四)
组合、继承。委托(类与类之间的关系) 复合 queue类里有一个deque,那么他们的关系叫做复合。右上角的图表明复合的概念。上图的特例表明,queue中的功能都是通过调用c进行实现(adapter)。 复合关系下的构造和…...
【4】深度学习之Pytorch——如何使用张量处理时间序列数据集(共享自行车数据集)
表格数据 表格中的每一行都独立于其他行,他们的顺序页没有任何关系。并且,没有提供有关行之前和行之后的列编码信息。 表格类型的数据是指通过表格的形式表示的数据,它以行和列的方式组织数据。表格中的每一行代表一个数据项,每…...
mulesoft MCIA 破釜沉舟备考 2023.02.10.01
mulesoft MCIA 破釜沉舟备考 2023.02.10.01 1. What is a defining charcateristic of an integration-Platform-as-a-Service(iPaaS)?2. An application deployed to a runtime fabric environment with two cluster replicas is designed to periodically trigger of flow f…...
干货 | PCB拼板,那几条很讲究的规则!
拼板指的是将一张张小的PCB板让厂家直接给拼做成一整块。一、为什么要拼板呢,也就是说拼板的好处是什么?1.为了满足生产的需求。有些PCB板太小,不满足做夹具的要求,所以需要拼在一起进行生产。2.提高SMT贴片的焊接效率。只需要过一…...
笔试题-2023-思远半导体-数字IC设计【纯净题目版】
回到首页:2023 数字IC设计秋招复盘——数十家公司笔试题、面试实录 推荐内容:数字IC设计学习比较实用的资料推荐 题目背景 笔试时间:2022.08.20应聘岗位:数字IC设计工程师笔试时长:90min笔试平台:牛客网题目类型:填空题(2道),不定项选择题(3道),单选题(2道),问…...
canvas根据坐标点位画图形-canvas拖拽编辑单个图形形状
首先在选中图形的时候需要用鼠标右击来弹出选择框,实现第一个编辑节点功能 在components文件夹下新建右键菜单 RightMenu文件: <template><div v-show"show" class"right-menu" :style"top:this.ypx;left:this.xpx…...
JavaEE 初阶 — 确认应答机制
文章目录确认应答机制(安全机制)1 什么是后发先至问题1 如何解决后发先至问题确认应答机制(安全机制) 确认应答 是实现可靠传输的最核心机制。 这里指的 可靠传输 不是说 100% 可以把消息发给接收方,而是尽力而为&…...
0207 事件
事件监听事件监听版本事件类型事件概念事件在编程时系统内发生的动作或者发生的事情例子点击按钮鼠标经过拖拽鼠标事件监听(注册事件,绑定事件)让程序员检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应…...
SpringBoot整合Swagger
目录 一、swagger介绍 二、springboot集成swagger 1、创建一个springboot-web项目 2、导入相关依赖 3、编写一个Hellow工程 4、配置swagger --->config 5、启动springboot工程 6、配置swagger信息 7、配置swagger扫描接口 8、如何设置Swagger在生产环境中使用&…...
20230210英语学习
Why Do So Many Cats Have White ‘Socks’ on Their Paws? 为什么好多猫咪脚上都“穿着白袜子”? If you see a house cat, the odds are high that it will have white paws, a look that many owners affectionately call "socks."But socks are rar…...
【图像处理OpenCV(C++版)】——4.5 全局直方图均衡化
前言: 😊😊😊欢迎来到本博客😊😊😊 🌟🌟🌟 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快…...
2022年API安全研究报告
目录 导读 2022年API安全风险概况 2022年平均每月遭受攻击的API数量超21万...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
