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

【C++杂货铺】多态


目录

🌈前言🌈

📁多态的概念

📁 多态的定义及实现

 📂 多态的构成条件

 📂 虚函数

 📂 虚函数重写

 📂 C++11 override 和 final

📂 重载,覆盖(重写),隐藏(重定义)的对比

📁 抽象类

 📂 概念

 📂 接口继承和实现继承

📁 多态的原理(重点)

 📂 虚函数表

 📂 多态的原理

 📂 动态绑定和静态绑定

📁 单继承和多继承关系中的虚表

 📂 单继承中的虚函数表

 📂 多继承中的虚函数表

 📂 菱形继承的虚函数表(了解)

 📂 菱形虚拟继承的虚函数表(了解)

📁 面试问题

📁 总结


🌈前言🌈

        欢迎观看本期【C++杂货铺】,本期内容将讲解C++作为面向对象语言的三大特性之一的多态,其中包含了多态的概念,定义及其实现;什么是抽象类;重点讲解多态的原理,以及应用,分别是是单继承和多继承关系中的虚函数表;此外讲解面试问题。

        面向对象语言有三大特性:封装,继承,多态,如果想要了解另外两个特性,欢迎阅览以下文章

【C++杂货铺】详解类和对象 [上]-CSDN博客

【C++杂货铺】继承-CSDN博客

📁多态的概念

        多态通俗来说,就是多种形态,具体点是去完成某个行为,当不同的对象去完成时会好惨上不同的状态。

        举个例子:买票这个行为,普通人买票时,是全价票;学生买票时,是半价票;军人买票是优先买票。

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

📁 多态的定义及实现

 📂 多态的构成条件

        1.  必须通过基类的指针或引用去调用虚函数。

        2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

 📂 虚函数

        被virtual修饰的类成员函数被称为虚函数。

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

 📂 虚函数重写

        派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值,函数名字,参数列表完全相同),成派生类的虚函数和从西饿了基类的虚函数。

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议
这样使用*//*void BuyTicket() { cout << "买票-半价" << endl; }*/
};void Func(Person& p)
{ p.BuyTicket(); 
}int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}

虚函数重写的两个例外:

        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 ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函
数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数。int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

 📂 C++11 override 和 final

        从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行期间时没有得到预期处理结果才来devbug会得不偿失,因此,C++11提供了override 和 final两个关键字,可以帮助用户检测是否重写。

        1. final:修饰虚函数,表示该虚函数不能被重写。

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

         2. override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

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

📂 重载,覆盖(重写),隐藏(重定义)的对比

📁 抽象类

 📂 概念

        在虚函数的后面写上=0,则这个函数称为纯虚函数,包含纯虚函数的类叫做抽象类(也叫做接口类),抽象类不能实例化对象。派生类继承后也不能实例化出对象,只能重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现了接口继承。

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 << "BMW-操控" << endl;}
};
void Test()
{Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();
}

 📂 接口继承和实现继承

        普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的函数的实现。

        虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义为虚函数。

📁 多态的原理(重点)

 📂 虚函数表

// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};

        通过调试,我们发现Base对象是8字节,除了_b成员,还多了一个_vfptr放在对象的前面(注意有些平台可能放到对象的最后面,这个与平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function )。一个含有虚函数的类中都至少有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也称作虚表。那么派生类中这个表中放了些什么。

// 针对上面的代码我们做出以下改造
// 1.我们增加一个派生类Derive去继承Base
// 2.Derive中重写Func1
// 3.Base再增加一个虚函数Func2和一个普通函数Func3
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. 派生类自己新添加的虚函数按其在派生类的声明次序添加到派生类虚表的最后。

6. 虚表存在哪里?和普通函数一样都是存在在代码段,只是它的指针又存到了虚表中;虚函数表存的是虚函数指针,不是虚函数,

 📂 多态的原理

class Person {
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}

1. 观察下图红色箭头我们可以看到,p是指向mike对象时,p->BuyTicket在mike的虚表中找到虚函数是Person::BuyTicket。

2. 观察下图蓝色箭头我们可以看到,p是指向johnson对象时,p->BuyTicket在johnson的续保中找到虚函数是Student::ButTicket。

        这样就实现了不同对象去完成同一个行为时,展现出不同形态。

3. 反过来思考我们为什么要达到多态,有两个条件:a. 虚函数的重写 ; b. 对象的指针或引用调用虚函数。

4. 通过汇编代码分析,看出满足多态条件后的函数调用,不是在编译时确定的,是运行起来以后到对象的虚表找,再调用的;不满足多态的函数调用是编译期间确定好的。

void Func(Person* p)
{p->BuyTicket();
}
int main()
{Person mike;Func(&mike);mike.BuyTicket();return 0;
}
// 以下汇编代码中跟你这个问题不相关的都被去掉了
void Func(Person* p)
{
...p->BuyTicket();
// p中存的是mike对象的指针,将p移动到eax中
001940DE  mov         eax,dword ptr [p]
// [eax]就是取eax值指向的内容,这里相当于把mike对象头4个字节(虚表指针)移动到了edx
001940E1  mov         edx,dword ptr [eax]
// [edx]就是取edx值指向的内容,这里相当于把虚表中的头4字节存的虚函数指针移动到了eax
00B823EE  mov         eax,dword ptr [edx]
// call eax中存虚函数的指针。这里可以看出满足多态的调用,不是在编译时确定的,是运行起来
以后到对象的中取找的。
001940EA  call        eax  
00头1940EC  cmp         esi,esp  
}
int main()
{
... 
// 首先BuyTicket虽然是虚函数,但是mike是对象,不满足多态的条件,所以这里是普通函数的调
用转换成地址时,是在编译时已经从符号表确认了函数的地址,直接call 地址mike.BuyTicket();
00195182  lea         ecx,[mike]
00195185  call        Person::BuyTicket (01914F6h)  
... 
}

 📂 动态绑定和静态绑定

        静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态。例如,函数重载。

        动态绑定也称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

📁 单继承和多继承关系中的虚表

 📂 单继承中的虚函数表

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;}virtual void func4() {cout<<"Derive::func4" <<endl;}
private :int b;
};

        监视窗口隐藏了派生类中func3 和 func4 两个虚函数,可以通过使用diamante打印出虚表中的函数。

typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{// 依次取虚表中的虚函数指针打印并调用。调用就可以看出存的是哪个函数cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Base b;Derive d;// 思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数
指针的指针数组,这个数组最后面放了一个nullptr// 1.先取b的地址,强转成一个int*的指针// 2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针// 3.再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。// 4.虚表指针传递给PrintVTable进行打印虚表// 5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最
后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再
编译就好了。VFPTR* vTableb = (VFPTR*)(*(int*)&b);PrintVTable(vTableb);VFPTR* vTabled = (VFPTR*)(*(int*)&d);PrintVTable(vTabled);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 vTable[])
{cout << " 虚表地址>" << vTable << endl;for (int i = 0; vTable[i] != nullptr; ++i){printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);VFPTR f = vTable[i];f();}cout << endl;
}
int main()
{Derive d;VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);PrintVTable(vTableb1);VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d+sizeof(Base1)));PrintVTable(vTableb2);return 0;
}

 📂 菱形继承的虚函数表(了解)

        菱形继承虚函数表的对象模型和多继承是相同的,但是菱形继承造成了数据冗余和二义性问题,所以这里就不做讲解。

 📂 菱形虚拟继承的虚函数表(了解)

        菱形虚继承中的虚函数表涉及到虚基表指针以及虚基表,虚基表指针指向虚基表,虚基表存放了虚表指针和公共类的偏移量,通过偏移量计算虚表指针和公共类的偏移量,解决了数据冗余和二义性,以及切片等问题。

        D中的虚函数会放到第一个虚函数表中,B的虚函数放到B的虚函数表中,C的虚函数放到C的虚函数表中,B和C的基类A的虚函数提取出来,单独创建一个虚函数表,存放共享类A的虚函数。

        理论上来说派生类不会有自己的虚函数表,派生类的虚函数会放到继承下来的基类的虚函数表中;但如果是上述菱形虚拟继承中的虚函数表,派生类会有自己的虚函数表。

📁 面试问题

1. 下面哪种面向对象的方法可以让你变得富有( A )

A: 继承 B: 封装 C: 多态 D: 抽象

2. ( D )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关, 而对方法的调用则可以关联于具体的对象。

A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定

3. 面向对象设计中的继承和组合,下面说法错误的是?( C

A:继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复 用,也称为白盒复用

B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动 态复用,也称为黑盒复用

C:优先使用继承,而不是组合,是面向对象设计的第二原则

D:继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封 装性的表现

4. 以下关于纯虚函数的说法,正确的是( A )

A:声明纯虚函数的类不能实例化对象

B:声明纯虚函数的类是虚基类

C:子类必须实现基类的纯虚函数

D:纯虚函数必须是空函数

5. 关于虚函数的描述正确的是( B )

A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型

B:内联函数不能是虚函数

C:派生类必须重新定义基类的虚函数

D:虚函数可以是一个static型的函数

6. 关于虚表说法正确的是( D

A:一个类只能有一张虚表

B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表

C:虚表是在运行期间动态生成的

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类使用的不是同一张虚表

8. 下面程序输出结果是什么? ( A )

#include<iostream>
using namespace std;
class A{
public:A(char *s) { cout<<s<<endl; }~A(){}
};
class B:virtual public A
{
public:B(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class C:virtual public A
{
public:C(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class D:public B,public C
{
public:D(char *s1,char *s2,char *s3,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

9. 多继承中指针偏移问题?下面说法正确的是( C )

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

10. 以下程序输出结果是什么( B

 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: 以上都不正确

1. 什么是多态?

2. 什么是重载、重写(覆盖)、重定义(隐藏)?

3. 多态的实现原理?

4. inline函数可以是虚函数吗?

        可以,不过编译器就忽略inline属性,这个函数就不再是 inline,因为虚函数要放到虚表中去。

5. 静态成员可以是虚函数吗?

        不能,因为静态成员函数没有this指针,使用类型::成员函数 的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。
6. 构造函数可以是虚函数吗?

        答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。

7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?

        可以,并且最好把基类的析构函数定义成虚函数。

8. 对象访问普通函数快还是虚函数更快?

        首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。

9. 虚函数表是在什么阶段生成的,存在哪的?

        虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

10. C++菱形继承的问题?虚继承的原理?

        【C++杂货铺】继承-CSDN博客 

        注意这里不要把虚函数表和虚基表搞混了。

11. 什么是抽象类?抽象类的作用?

       在虚函数的后面写上=0,则这个函数称为纯虚函数,包含纯虚函数的类叫做抽象类(也叫做接口类),抽象类不能实例化对象。抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。

📁 总结

        以上,就是本期【C++杂货铺】的主要内容了,讲解了C++中三大特性之一的多态,什么是多态,如何实现多态,多态的底层原理,此外讲解了单继承的虚函数表,多继承的虚函数表,了解了菱形虚拟继承的虚函数表。

        如果感觉本期内容对你有帮助,欢迎点赞,收藏,关注Thanks♪(・ω・)ノ

相关文章:

【C++杂货铺】多态

目录 &#x1f308;前言&#x1f308; &#x1f4c1;多态的概念 &#x1f4c1; 多态的定义及实现 &#x1f4c2; 多态的构成条件 &#x1f4c2; 虚函数 &#x1f4c2; 虚函数重写 &#x1f4c2; C11 override 和 final &#x1f4c2; 重载&#xff0c;覆盖&#xff08;重写…...

(学习日记)2024.04.20:UCOSIII第四十八节:各文件功能概览

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…...

数据结构-二叉树-堆(二)

一、建堆的时间复杂度问题 1、除了向上调整建堆&#xff0c;我们还可以向下调整建堆。不能在根上直接开始向下调整。这里的条件就是左右子树必须都是大堆或者小堆。我们可以倒着往前走&#xff0c;可以从最后一个叶子开始调整。但是从叶子开始调整没有意义。所以我们可以从倒数…...

身份证二要素核验介绍及使用方法

一、身份证二要素核验简介及重要性 身份证二要素核验是一种重要的身份验证技术&#xff0c;它在现代社会中发挥着至关重要的作用&#xff0c;特别是在涉及个人信息安全和隐私保护的领域。通过身份证二要素核验&#xff0c;我们可以有效地确认个人身份的真实性&#xff0c;从而…...

探索 去中心化的Web3.0

随着区块链技术的日益成熟和普及&#xff0c;Web3&#xff08;Web 3.0&#xff09;已经成为一个无法忽视的趋势。Web3不仅仅是一个技术概念&#xff0c;更是一个去中心化、透明、用户数据拥有权归还给用户的互联网新时代。在这篇文章中&#xff0c;我们将深入探讨Web3技术的核心…...

递归的层序遍历

最近遇到一个业务需求&#xff1a;一颗依赖树&#xff0c;其实就是一颗递归树&#xff0c;如何一层一层的数据放在一起&#xff0c;可以近似理解为二叉树的层序遍历。 业务理解为递归树的层序遍历 代码示例&#xff1a; public class RecursionErgodic {public static void…...

pytest使用 pytest-rerunfailures 插件实现失败用例重跑功能

使用 pytest 进行测试时&#xff0c;你可以通过安装并配置 pytest-rerunfailures 插件来实现失败用例重跑功能。以下是一个示例说明&#xff1a; 假设你有一个测试文件 test_example.py 包含如下测试用例&#xff1a; import pytestpytest.mark.parametrize("num",…...

2024/4/23 C++day1

有以下定义&#xff0c;说明哪些量可以改变哪些不可以改变&#xff1f; const char *p; 指针可以改变 值不可以改变 const (char *) p; 语法错误 char *const p; 指针不可以改变 值可以改变 const char* const p; 指针和值…...

OpenHarmony鸿蒙南向开发案例:【智能窗户通风设备】

样例简介 本文档介绍了安全厨房案例中的相关智能窗户通风设备&#xff0c;本安全厨房案例利用轻量级软总线能力&#xff0c;将两块欧智通V200Z-R/BES2600开发板模拟的智能窗户通风设备和燃气告警设备组合成。当燃气数值告警时&#xff0c;无需其它操作&#xff0c;直接通知软总…...

解析‘找不到vcruntime140.dll,无法继续执行代码’的异常修复方法

找不到vcruntime140.dll,无法继续执行代码&#xff1f;这是小事情&#xff0c;这个情况主要是vcruntime140.dll文件丢失了&#xff0c;导致一些程序没办法正常的运行&#xff0c;我们只要修复好这个vcruntime140.dll,文件就可以了。下面一起来了解一下。 一.找不到vcruntime140…...

Golang对接Ldap(保姆级教程:概念搭建实战)

Golang对接Ldap&#xff08;保姆级教程&#xff1a;概念&搭建&实战&#xff09; 最近项目需要对接客户的LDAP服务&#xff0c;于是趁机好好了解了一下。LDAP实际是一个协议&#xff0c;对应的实现&#xff0c;大家可以理解为一个轻量级数据库。用户查询。比如&#xff…...

Java23种设计模式-创建型模式之工厂方法模式

工厂方法模式&#xff08;Factory Method Pattern&#xff09; 一种创建型设计模式&#xff0c;它定义了一个用于创建对象的接口&#xff0c;让子类决定将哪一个类实例化&#xff0c;从而将产品的实例化推迟到子类中。这种模式的主要角色包括&#xff1a; 角色1&#xff1a;抽…...

Oracle故障处理:ORA-00600错误处理思路

提前说明&#xff1a; 该故障&#xff0c;我只是旁观者。 但处理该故障的DBA工程师&#xff0c;思路很清晰&#xff0c;我非常受教&#xff01;在此也将经验分享。 目录 项目场景 问题分析 优化建议 项目场景 在某项目数据库运维群&#xff0c;有现场同事发了张报错截图如下…...

微信小程序使用 Vant Weapp 中 Collapse 折叠面板 的问题!

需求&#xff1a;结合Tab 标签页 和 Collapse 折叠面板 组合成显示课本和章节内容&#xff0c;并且用户体验要好点&#xff01; 如下图展示&#xff1a; 问题&#xff1a;如何使用Collapse 折叠面板 将内容循环展示出来&#xff1f; js中的数据是这样的 代码实现&#xff1…...

论文写作神器:用ChatGPT写论文的5大高效技巧

在人工智能日渐成熟的今天&#xff0c;ChatGPT已经成为学术界、业界乃至日常生活中不可或缺的工具之一。尤其是对于学生和研究人员而言&#xff0c;ChatGPT能大幅度提高论文写作的效率和质量。然而&#xff0c;许多人尚未掌握如何高效利用这一工具&#xff0c;很多人用chatgpt写…...

微信小程序展示倒计时

html <view class"countdown"> <text>倒计时&#xff1a;</text> <text wx:for"{{countdown}}" wx:key"index">{{item}}</text> </view> ts data: {countdown: [], // 存放倒计时数组 targetTime:…...

什么是用户体验(UX)文案,为什么它很重要?

网上购物如今比以往任何时候都更加相关。所以我们将以此为例说明什么是用户体验&#xff08;UX&#xff09;文案&#xff0c;以及为什么它很重要。 假设你去了一个在线商店。你需要执行一系列操作&#xff1a; 找到合适的部分选择你感兴趣的产品弄清楚它们是什么&#xff0c;…...

算法06链表

算法06链表 一、链表概述1.1概述1.2链表的组成部分&#xff1a;1.3链表的优缺点&#xff1a; 二、链表典例力扣707.设计链表难点分析&#xff1a;&#xff08;1&#xff09;MyLinkedList成员变量的确定&#xff1a;&#xff08;2&#xff09;初始化自定义链表&#xff1a;&…...

第十七章 数据管理和组织变革管理

17.2 变革法则 1&#xff09;组织不变革&#xff0c;人就变。 2&#xff09;人们不会抗拒变革&#xff0c;但抵制被改变。 3&#xff09;事情之所以存在是惯性所致。 4&#xff09;除非有人推动变革&#xff0c;否则很可能止步不前。 5&#xff09;如果不考虑人的因素&#xf…...

基于harris角点和RANSAC算法的图像拼接matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ....................................................................... I1_harris fu…...

C++感受6-Hello World 交互版

变量、常量输入、输出、流getline() 函数读入整行输入Hello() 函数复习新定义函数 Input() 实现友好的人机交互还有 “痘痘” 为什么挤不到的分析…… 1. DRY 原则简介 上一节课&#xff0c;我们写了两版“问候”程序。第一版的最大问题是重复的内容比较多&#xff0c;每一次问…...

02_c/c++开源库ZeroMQ

1.安装 C库 libzmq sudo apt install libzmq3-dev 实例: https://zeromq.org/get-started/?languagec&librarylibzmq# 编译依赖: pkg-config --cflags --libs libzmq or cat /usr/lib/x86_64-linux-gnu/pkgconfig/libzmq.pc -isystem /usr/include/mit-krb5 -I/usr/in…...

计算机视觉 CV 八股分享 [自用](更新中......)

目录 一、深度学习中解决过拟合方法 二、深度学习中解决欠拟合方法 三、梯度消失和梯度爆炸 解决梯度消失的方法 解决梯度爆炸的方法 四、神经网络权重初始化方法 五、梯度下降法 六、BatchNorm 七、归一化方法 八、卷积 九、池化 十、激活函数 十一、预训练 十二…...

【MHA】MySQL高可用MHA源码1-主库故障监控

1 阅读之前的准备工作 1 一个IDE工具 &#xff0c;博主自己尝试了vscode安装perl的插件&#xff0c;但是函数 、变量 、模块等都不能跳转&#xff0c;阅读起来不是很方便。后来尝试使用了pycharm安装perl插件&#xff0c;阅读支持跳转&#xff0c;自己也能写一些简单的测试样例…...

如何一键清除文件目录下所有的node_modules

如何一键清除文件目录下所有的node_modules 快速删除目录下的node_modules&#xff0c;下面附上windows和mac的脚本指令 windows脚本 FOR /d /r . %d in (node_modules) DO IF EXIST "%d" rm -rf "%d"mac脚本 find . -name "node_modules" -…...

【产品经理修炼之道】- 导航架构设计

目录 一、导航是什么 二、导航的作用 三、导航的分类 四、导航菜单的广度与深度 五、导航的颜色 六、导航的形态 七、导航的研究 八、导航的设计 九、导航改版案例分享 总结 每个网页的设计都需要包括导航&#xff0c;那么导航架构该如何设计&#xff1f;作者结合之前…...

本地部署和运行大型语言模型(Large Language Models, LLMs)的工具Ollama

文章目录 本地部署和运行大型语言模型&#xff08;Large Language Models, LLMs&#xff09;的工具Ollama背景什么是Ollama主要功能优势 使用场景Ollama LangChain 实现本地运行Llama 3 本地部署和运行大型语言模型&#xff08;Large Language Models, LLMs&#xff09;的工具…...

Python-100-Days: Day01

Day01 Python简介 1.1989年Guido von Rossum在圣诞节之夜开始着手python语言编译器的编写。 2.1991年2月 Python v1 编译器诞生&#xff0c;使用C实现的&#xff0c;此时可以调用C的库函数。 3.1994年1月&#xff0c;Python v1.0 正式版发布。 4.2000年10月16日&#xff0…...

g 对象:Flask 应用中的“临时口袋”

文章目录 g对象的理解Flask 中的 g 对象g 对象的特点:使用 g 对象: 示例 g对象的理解 想象一下&#xff0c;你在逛超市。你需要一个购物篮来装你挑选的商品。这个购物篮就像 Flask 应用中的 g 对象&#xff0c;它是一个临时存放东西的地方&#xff0c;方便你在购物过程中随时取…...

JavaEE初阶——多线程(七)——定时器

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程的第七篇文章——关于定时器 如果有不足的或者错误的请您指出! 目录 4.定时器4.1标准库提供的定时器4.2自己实现一个定时器4.2.1任务类4.2.2Timer类4.2.3 有一个线程来负…...

嵌入式4-24

作业&#xff1a; 整理思维导图 定义一个矩形类Rec&#xff0c;包含私有属性length&#xff0c;width&#xff0c;有以下成员函数&#xff1a; void set_length(int l); //设置长度 void set_width(int w); //设置宽度 int get_length(); //获取长度 int get_width(); //获取宽…...

跟我学C++中级篇——临时对象

一、临时对象 Temporary object&#xff0c;临时对象。一听名字就明白&#xff0c;这个对象的意义不大&#xff0c;只是临时中转一下或者存在一下&#xff0c;有的可能连个存在感都刷不到就消失了。但不要小看这种临时对象&#xff0c;对C/C这种以效率严苛为前提的编程环境下&…...

【S32K3 MCAL配置】-7.1-GPT Driver:定时器中断-创建一个周期执行的任务

"><--返回「Autosar_MCAL高阶配置」专栏主页--> 案例背景:常用于周期点亮/关闭一个LED灯;或者精度一般的占空比为50% PWM方波;或者周期调用一个函数,在该函数中我们可以执行一些软件策略(简易的OS)。 目录(共15页精讲,基于评估板: NXP S32K312EVB-Q172,…...

java可盈保险合同管理系统的设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的可盈保险合同管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Spring Boot的…...

【智能算法】囊状虫群算法(TSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2020年&#xff0c;S Kaur等人受到囊状虫群自然行为启发&#xff0c;提出了囊状虫群算法&#xff08;Tunicate Swarm Algorithm, TSA&#xff09;。 2.算法原理 2.1算法思想 TSA模拟了囊状虫群在导…...

python基础——正则表达式

&#x1f4dd;前言&#xff1a; 这篇文章主要想讲解一下python中的正则表达式&#xff1a; 1&#xff0c;什么是正则表达式 2&#xff0c;re模块三匹配 3&#xff0c;元字符匹配 4&#xff0c;具体示例 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&am…...

T1级,生产环境事故—Shell脚本一键备份K8s的YAML文件

大家好&#xff0c;我叫秋意零。 最近对公司进行日常运维工作时&#xff0c;出现了一个 T1 级别事故。导致公司的“酒云网”APP的无法使用。我和我领导一起搞了一个多小时&#xff0c;业务也停了一个多小时。 起因是&#xff1a;我的部门直系领导&#xff0c;叫我**删除一个 …...

C语言程序设计:预处理命令

预处理命令 基础知识 预处理命令简介 C语言的预处理命令是指编译之前由预处理器执行的指令&#xff0c;用于在源代码中进行一些预处理操作。 常见预处理命令 (1) #define 定义一个宏&#xff0c;用于替换源代码中的标识符为指定的文本。 #define MAX_NUM 100 int arr[MAX_NU…...

C++ 中的 struct 和 Class

通常struct用于表示一组相关的数据&#xff0c;而Class用于表示一个封装了数据和操作的对象。如果只是用于来组织一些数据&#xff0c;而不涉及复杂的封装和继承关系&#xff0c;则struct更为直观&#xff1b;如果需要封装、继承等面向对象编程的特性&#xff0c;可以选择使用C…...

基于Qt的二维码生成与识别

基于Qt的二维码生成与识别 一、获取QZxing开源库 1.通过封装的QZxing开源库生成和识别二维码&#xff0c;下载地址&#xff1a;GitCode - 开发者的代码家园https://gitcode.com/mirrors/ftylitak/qzxing/tree/master。 2.下载解压后&#xff0c;使用Qt Creator xx&#xff0…...

docker 基本命令

目录 一、docker 镜像操作命令 1.1.查询软件镜像 1.2.docker pull&#xff1a;下载镜像 1.3.docker push&#xff1a;上传镜像 1.4.docker images&#xff1a;查看本地镜像 1.5.docker inspect &#xff1a;获取镜像详细信息 1.6.docker tag&#xff1a;添加镜像标签 …...

h5键盘弹出收起时引起的页面变化

h5键盘弹出收起时引起的页面变化 键盘弹出时会导致窗口发生变化&#xff0c;置于底部的操作项会被顶上来&#xff0c;所以在键盘弹出的时候处理一下页面节点 通过监听页面窗口大小变化判断键盘状态键盘弹出时隐藏底部操作项在页面加载完成时执行即可 export function keyboa…...

Redis入门到实战教程(基础篇)笔记

教学来源&#xff1a; Redis课程介绍导学_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1cr4y1671t?p1一、Redis 入门 1.认识NoSQL 2.Redis在虚拟机中的安装和开机自启 Redis在虚拟机中安装和配置开机自启-CSDN博客https://blog.csdn.net/qq_69183322/article/deta…...

启动MySQL服务

在 Windows 系统上&#xff1a; 首先&#xff0c;找到 MySQL 安装目录&#xff0c;一般默认是在 C:\Program Files\MySQL 文件夹下。进入该目录下的 bin 文件夹。找到 mysqld.exe 文件&#xff0c;双击运行它。 在 Linux 系统&#xff08;以 CentOS 为例&#xff09;&#xff…...

Windows上构建 Chisel-Bootcamp

windows环境构建本地Chisel-Bootcamp 安装摘要Chisel-boocamp环境搭建安装java安装Anaconda安装scala 下载Chisel-bootcamp 环境Reference 安装摘要 在windows上安装chisel-boocamp&#xff0c;与linux过程类似。 安装java8安装anaconda安装scala下载Chisel-bootcamp环境 Ch…...

Spring Bean依赖注入-Spring入门(二)

1、SpringBean概述 在Spring中&#xff0c;一切Java对象都被视为Bean&#xff0c;用于实现某个具体功能。 Bean的依赖关系注入的过程&#xff0c;也称为Bean的装配过程。 Bean的装配方式有3种&#xff1a; XML配置文件注解Java类 Spring中常用的两种装配方式分别是基于XML的…...

java中spring底层核心原理解析(1)

相关系列 java中spring底层核心原理解析(2)-CSDN博客 总起 本章主要是讲以下的内容 Bean的生命周期底层原理依赖注入底层原理初始化底层原理推断构造方法底层原理 先看spring入门代码&#xff1a; ClassPathXmlApplicationContext context new ClassPathXmlApplicationCo…...

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…...

科技渔业,智慧守护:4G+北斗太阳能定位终端准确定位,防拆卸报警,夯实渔业管理水平

如何高效地管理渔船&#xff0c;有效监控禁渔区域&#xff0c;4G北斗太阳能定位终端应运而生&#xff0c;成为渔业管理的重要应用工具。 我国作为全球渔业的重要国家&#xff0c;渔业一直是沿海地区传统的支柱产业&#xff0c;对经济的繁荣和民生的稳定起着至关重要的作用。因…...

【Elasticsearch】Elasticsearch 从入门到精通(二):基础使用

《Elasticsearch 从入门到精通》共包含以下 2 2 2 篇文章&#xff1a; Elasticsearch 从入门到精通&#xff08;一&#xff09;&#xff1a;基本介绍Elasticsearch 从入门到精通&#xff08;二&#xff09;&#xff1a;基础使用 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的…...

抖音小店有订单后怎么发货?实操分享!发货全流程来了

哈喽~我是电商月月 做无货源抖音小店的店铺在出单后怎么发货&#xff1f;今天我就来给大家解答这个问题&#xff0c;其中的注意事项新手商家可以收藏一下&#xff0c;避免犯错 抖音小店的商品出单后&#xff0c;商家在“管理中心-订单管理”页面就能看见所有待处理的订单 一…...

Rust和Go语言在2024年的对比

Which is better, Rust or Go? Which language should you choose for your next project and why? How do they compare in terms of performance, simplicity, security, features, scalability, and concurrency? What do they have in common and what are their fundam…...

宏内核与微内核

目录 进程间通信 安全与稳定 I/O通信 可扩展性和可移植性 这是小文章分享&#xff0c;所有的中文都是机翻&#xff08;&#xff09; 可以看看&#xff1a;http://web.cs.wpi.edu/~cs3013/c12/Papers/Roch_Microkernels.pdf 大致来说&#xff0c;操作系统本身由两部分组成&am…...

网关过滤器实现接口签名检验

背景 往往项目中的开放接口可能被别有用心者对其进行抓包然后对请求参数进行篡改&#xff0c;或者重复请求占用系统资源为此我们行业内使用比较多的策略是接口签名校验。签名校验的实现可以用注解aop的形式实现&#xff0c;也可以使用过滤器统一拦截校验实现&#xff0c;此篇文…...

记录一下自己的宏碁暗影骑士电脑的属性

TOC 前言 没有前言。 参考博文 怎么查自己电脑服务器信息吗,如何查看自己电脑的服务器 一、cmd 看到服务器型号 wmic csproduct get name查询CPU个数 按照博主的方法&#xff0c;我出现了报错。 在 Windows 上&#xff0c;您可以通过 PowerShell 来执行类似的操作。您可以…...

【C++】string类的使用①(默认成员函数 || 迭代器接口begin,end,rbegin和rend)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f308;关于string类&#x1f308;string类的成员函数&#x1f525;默认成员函数string类对象的构造(constructor)string类对象的析构string类对象的赋值运算符…...

RocketMQ 面试题(二)

1. 列举RocketMQ发送的三种策略 &#xff1f; RocketMQ提供了三种主要的消息发送策略&#xff0c;它们分别是同步发送&#xff08;Sync&#xff09;、异步发送&#xff08;Async&#xff09;和单向发送&#xff08;OneWay&#xff09;。以下是关于这三种发送策略的详细解释&am…...

C++ static_cast学习

static_cast可实现&#xff0c; 1 基本类型之间的转换 2 void指针转换为任意基本类型的指针 3 用于有继承关系的子类与父类之间的指针或引用的转换 用于基本类型转化时&#xff0c;会损失精度类似于C语言的强制转化&#xff1b; 下面先看一下void指针的转换&#xff1b; …...

PostgreSQL扩展之PGroonga:多语言全文搜索

简介 PGroonga 是一个 PostgreSQL 扩展&#xff0c;它增加了基于 Groonga 的全文搜索索引方法。虽然原生的 PostgreSQL 支持全文索引&#xff0c;但它仅限于基于字母和数字的语言。PGroonga 提供了更广泛的字符支持&#xff0c;使其成为 PostgreSQL 支持的语言的超集&#xff…...

Vue.directive注册(全局与局部)自定义指令使用详解与实战

指令定义函数提供了几个钩子函数&#xff08;可选&#xff09;&#xff1a; bind: 只调用一次&#xff0c;指令第一次绑定到元素时调用&#xff0c;可以定义一个在绑定时执行一次的初始化动作。inserted: 被绑定元素插入父节点时调用&#xff08;父节点存在即可调用&#xff0…...

【2024.05.15_周三_晴】

当了一天的答辩秘书&#xff0c;熟悉了整个流程&#xff0c;以及对论文的成果应该包含的内容有了较清晰认识。 明日计划&#xff1a; 时间内容8:00起床8:00-12:30算法作业13:30-19:00go第7章、八章19:00-22:00宿舍团建22:00-23:00答辩后续工作...

SIGPIPE信号与SO_REUSEADDR

SIGPIPE 当向一个已经关掉的管道写数据&#xff0c;write系统调用会返回一个 SIGPIPE 信号&#xff0c;该信号默认结束进程。 对于网络IO来说&#xff0c;当我们关闭了一个连接时&#xff0c;此时我们再往连接中写入数据&#xff0c;那么也会收到一个 SIGPIPE 信号。 对于这…...