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

【C++】多态详解

💗个人主页💗
⭐个人专栏——C++学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

一、多态概念

二、多态的定义及实现

1. 多态的构成条件

2. 虚函数

 2.1 什么是虚函数

 2.2 虚函数的重写

 2.3 虚函数重写的两个例外

2.3.1 协变

2.3.2 析构函数的重写

3. override 和 final

4. 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

1. 概念

2. 接口继承和实现继承

四、多态的原理

1. 虚函数表

2. 多态的原理

3. 动态绑定与静态绑定

五、单继承和多继承关系的虚函数表

1. 单继承中的虚函数表

2. 多继承中的虚函数表


一、多态概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。

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

多态的优势在于它增加了代码的灵活性和可扩展性。通过多态,我们可以编写通用的代码,处理多种类型的对象,使得程序更易于理解、扩展和维护。

二、多态的定义及实现

1. 多态的构成条件

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

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

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

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

2. 虚函数

 2.1 什么是虚函数

虚函数(virtual function)是面向对象编程中的一个概念,它是用来实现多态的机制之一。

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

虚函数是在基类中声明的一种特殊的成员函数,用关键字virtual进行声明。当在派生类中重写(override)这个函数时,可以使用关键字override来显式标识。

通过将函数声明为虚函数,可以实现动态绑定(dynamic binding),即在运行时根据对象的实际类型来调用相应的函数。这意味着当通过基类指针或引用调用虚函数时,实际调用的是对应的派生类中的函数。

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

2.2 虚函数的重写

虚函数的重写是指在派生类中重新定义(override)基类中已经声明的虚函数。通过重写虚函数,可以在派生类中提供自己的实现,以替代基类中的默认实现。

重写虚函数的规则如下:

  1. 基类中的虚函数必须使用关键字virtual进行声明。
  2. 派生类中的重写函数必须具有相同的名称、参数列表和返回类型。
  3. 派生类中的重写函数必须使用关键字override进行标识,以确保编译器进行正确的检查。
  4. 重写函数可以被声明为虚函数,但这不是必需的。
class Animal 
{
public:virtual void makeSound() {cout << "Animal is making a sound" << endl;}
};class Dog : public Animal 
{
public:void makeSound() override { // 重写虚函数cout << "Dog is barking" << endl;}
};class Cat : public Animal 
{
public:void makeSound() override { // 重写虚函数cout << "Cat is meowing" << endl;}
};int main() 
{Animal* animal = new Animal();Animal* dog = new Dog();Animal* cat = new Cat();animal->makeSound(); // 输出 "Animal is making a sound"dog->makeSound(); // 输出 "Dog is barking"cat->makeSound(); // 输出 "Cat is meowing"delete animal;delete dog;delete cat;return 0;
}

在上述代码中,Animal类中的makeSound()函数被声明为虚函数。派生类Dog和Cat分别重写了这个函数,并提供了自己的实现。

在main()函数中,通过基类指针调用makeSound()函数时,实际上会根据指针指向的对象类型来调用相应的派生类中的函数。这就是虚函数的重写实现多态性的一种方式。

 2.3 虚函数重写的两个例外

2.3.1 协变

虚函数的协变(covariant)是指派生类可以返回比基类更具体的类型。

换句话说,派生类可以重写基类中的虚函数,并返回一个派生类类型的指针或引用,而不仅仅是基类类型的指针或引用。

协变发生在满足以下条件时:

  1. 基类中的虚函数必须返回指针或引用类型。
  2. 派生类中重写的函数可以返回指向派生类的指针或引用类型,该派生类是基类中返回类型的派生类。
class Animal 
{
public:virtual Animal* create() {return new Animal();}
};class Dog : public Animal 
{
public:Dog* create() override { // 协变的重写函数return new Dog();}
};class Cat : public Animal 
{
public:Cat* create() override { // 协变的重写函数return new Cat();}
};int main() 
{Animal* animal = new Animal();Animal* dog = new Dog();Animal* cat = new Cat();Animal* newAnimal = animal->create();Animal* newDog = dog->create();Animal* newCat = cat->create();delete animal;delete dog;delete cat;delete newAnimal;delete newDog;delete newCat;return 0;
}

在上述代码中,Animal类中的create()函数返回一个指向Animal类型的指针。派生类Dog和Cat分别重写了这个函数,并返回指向Dog和Cat类型的指针。

在main()函数中,通过调用基类指针的create()函数,实际上会调用对应派生类中重写的create()函数。由于协变的存在,可以将返回的指针赋值给指向基类的指针,这样便保留了派生类的类型信息。

注意,为了实现协变,返回类型必须是指针或引用,而不能是值类型。否则无法满足派生类返回类型更具体的要求。

2.3.2 析构函数的重写

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字, 都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同, 看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

以下是一个示例,展示了虚函数中的析构函数重写是没有意义的:

class Animal 
{
public:Animal() {cout << "Animal constructor called." << endl;}virtual ~Animal() {cout << "Animal destructor called." << endl;}virtual void speak() {cout << "Animal speaks." << endl;}
};class Dog : public Animal 
{
public:Dog() {cout << "Dog constructor called." << endl;}~Dog() {cout << "Dog destructor called." << endl;}void speak() override {cout << "Dog barks." << endl;}
};int main() 
{Animal* animal = new Dog();animal->speak();delete animal;return 0;
}

Animal类定义了一个虚函数speak(),而派生类Dog中重写了该函数。在main()函数中,通过基类指针创建了一个Dog对象,然后调用speak()函数。

从结果可以看出,析构函数按照预期顺序调用,无需在派生类中显式重写。同时,虚函数speak()也可以正确地根据对象的实际类型调用对应的版本。

3. override 和 final

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

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

class Base 
{
public:virtual void foo() final {// 函数实现}
};class Derived : public Base 
{
public:void foo() override { // 编译错误,不能重写被声明为final的虚函数// 函数实现}
};

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

class Base 
{
public:virtual void foo() {// 函数实现}
};class Derived : public Base 
{
public:void foo() override { // 使用override关键字重写基类的虚函数// 函数实现}
};

4. 重载、覆盖(重写)、隐藏(重定义)的对比

三、抽象类

1. 概念

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

抽象类可以用于实现接口的一致性和多态性。通过基类指针或引用,可以在运行时动态地选择调用派生类的实现。

class Animal 
{
public:virtual void makeSound() = 0; // 纯虚函数void sleep() {cout << "Zzz..." << endl;}
};class Dog : public Animal 
{
public:void makeSound() override {cout << "Woof!" << endl;}
};class Cat : public Animal {
public:void makeSound() override {cout << "Meow!" << endl;}
};int main() 
{Animal* animalPtr = new Dog();animalPtr->makeSound(); // 输出 "Woof!"animalPtr->sleep(); // 输出 "Zzz..."delete animalPtr;animalPtr = new Cat();animalPtr->makeSound(); // 输出 "Meow!"animalPtr->sleep(); // 输出 "Zzz..."delete animalPtr;return 0;
}

 Animal是一个抽象类,包含一个纯虚函数makeSound()和一个普通的成员函数sleep()DogCatAnimal的派生类,必须实现makeSound()函数。通过基类指针,可以调用派生类的实现。

需要注意的是,无法直接实例化抽象类,因为它包含纯虚函数没有具体的实现。派生类必须实现所有纯虚函数,才能被实例化。抽象类可以作为基类来定义其他类,从而实现代码的复用和扩展。

2. 接口继承和实现继承

接口继承和实现继承是面向对象编程中的两种继承方式。

  1. 接口继承(Interface Inheritance)是指一个接口(interface)可以继承自另一个接口,继承接口的子接口会继承父接口的所有方法定义。接口继承主要用于定义一组共享的方法规范,子接口可以继续扩展这些方法规范,同时可以添加自己的方法规范。接口继承可以实现多继承的效果,一个类可以同时实现多个接口。

  2. 实现继承(Implementation Inheritance)是指一个类(class)可以继承自另一个类,继承类会继承父类的属性和方法。实现继承主要用于类之间的层级关系,子类可以继承父类的行为和状态,同时还可以添加自己特有的行为和状态。实现继承是一种单继承的方式,一个类只能直接继承一个父类。

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

四、多态的原理

1. 虚函数表

我们来看下面的代码,想一下sizeof(a)是多少。

class A
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};
int main()
{A a;cout << sizeof(a) << endl;return 0;
}

第一眼看去,这不就是4吗,我们来看一下运行结果:

为什么呢?我们打开监视窗口来看一下:

我们发现,a里面不仅仅存在一个_b,还有一个_vfptr放在对象的前面(注意有些 平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。

一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数 的地址要被放到虚函数表中,虚函数表也简称虚表。

 那么派生类中这个表放了些什么呢?我们接着往下分析,针对上面的代码我们做出以下改造

  1. 我们增加一个派生类Derive去继承Base 
  2. Derive中重写Func1 
  3. Base再增加一个虚函数Func2和一个普通函数Func3
class A
{
public:virtual void Func1(){cout << "A::Func1()" << endl;}virtual void Func2(){cout << "A::Func2()" << endl;}void Func3(){cout << "A::Func3()" << endl;}
private:int _a = 1;
};
class B : public A
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _b = 2;
};
int main()
{A a;B b;cout << sizeof(a) << endl;cout << sizeof(b) << endl;return 0;
}

我们再来看下这个代码结果是多少呢:

这又是为什么呢?我们继续来看一下调试窗口:

通过观察和测试,我们发现了以下几点问题:

  1. 派生类对象b中也有一个虚表指针,b对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就是存在部分的另一部分是自己的成员。
  2. 基类a对象和派生类b对象虚表是不一样的,这里我们发现Func1完成了重写,所以b的虚表中存的是重写的B::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
  3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函 数,所以不会放进虚表。
  4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。

总结一下派生类的虚表生成:

  • 先将基类中的虚表内容拷贝一份到派生类虚表中
  • 如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
  • 派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的?

答:虚函数存在虚表,虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。

注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。那么虚表存在哪的呢?实际我们去验证一下会发现vs下是存在代码段的。

2. 多态的原理

上面分析了这个半天了那么多态的原理到底是什么?我们来继续探索。

class A
{
public:virtual void Func1(){cout << "A::Func1()" << endl;}
private:int _a = 1;
};
class B : public A
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _b = 2;
};
int main()
{A a;B b;return 0;
}

 我们会疑惑,为什么多态可以实现指向父类调用父类函数 ,指向子类调用子类函数?

我们来看看虚表里的指针内容:

在监视窗口里我们可以看到a对象和b对象里的内容,我们继续打开内存窗口,取地址a,

我们看到第一个内容就是_vfptr的地址,继续通过_vfptr的地址,我们就能看到虚表里的内容,这时我们又看到了虚函数的地址。

我们来取地址b,发现和上诉同理。

但是,这时我们发现怎么在B类里的A的虚函数地址,和它原本在A类里的地址不一样,这就是虚函数的重写覆盖。

当一个类声明了虚函数时,编译器会为该类生成一个虚函数表。虚函数表是一个存储了虚函数指针的表格,每个包含虚函数的类都会有自己的虚函数表。虚函数表的每个表项存储了相应虚函数的地址。对于派生类,虚函数表中的表项可能会被重写以指向派生类中的虚函数。

当使用基类指针或引用调用虚函数时,编译器会根据对象的实际类型来查找虚函数表,并调用相应的虚函数。这种通过虚函数表来实现动态绑定的机制使得C++中的对象能够在运行时根据实际类型来确定调用的函数,而不是在编译时就确定下来。

3. 动态绑定与静态绑定

动态绑定和静态绑定是两种不同的函数调用机制,它们在编程语言中起到的作用也不同。

  • 静态绑定(静态分派): 静态绑定是在编译时期完成的,也称为早期绑定。在静态绑定中,函数调用的目标函数是在编译时根据对象的静态类型确定的。当调用一个函数时,编译器会根据对象的声明类型来确定要调用的函数,而不管对象的实际类型。

静态绑定适用于非虚函数和静态函数,这些函数的调用目标不会发生改变。在静态绑定中,对象的方法调用是基于声明类型进行的,也就是编译期间根据对象的静态类型来确定调用哪个函数,无法体现多态性。

  • 动态绑定(动态分派): 动态绑定是在运行时期完成的,也称为晚期绑定。在动态绑定中,函数调用的目标函数是根据对象的实际类型来确定的。当调用一个虚函数时,编译器会根据对象的实际类型来确定要调用的函数。

动态绑定适用于虚函数,虚函数表(vtable)是实现动态绑定的一种机制。每个包含虚函数的类都会有一个虚函数表,该表存储了类中虚函数的地址。运行时系统通过指向基类的指针或引用,查找对象实际的类型,并根据其虚函数表来确定要调用的函数。

动态绑定允许程序在运行时根据对象的实际类型来选择调用的函数,实现了多态性。这样可以实现基于对象的具体行为,并提高代码的灵活性。

五、单继承和多继承关系的虚函数表

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

观察下图中的监视窗口中我们发现看不见func3和func4。这里是编译器的监视窗口故意隐藏了这 两个函数,也可以认为是他的一个小bug。那么我们如何查看d的虚表呢?下面我们使用代码打印 出虚表中的函数。

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;
};
typedef void(*VFTPTR)();void PrintVFPtr(VFTPTR a[])
{for (size_t i = 0; a[i] != 0; i++){printf("a[%d]:%p\n", i, a[i]);}cout << endl;
}int main()
{Base b;Derive d;PrintVFPtr((VFTPTR*)*(int*)&b);PrintVFPtr((VFTPTR*)*(int*)&d);return 0;
}

 

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 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;
}

观察下图可以看出:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中

相关文章:

【C++】多态详解

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 一、多态概念 二、多态的定义及实现 1. 多态的构成条件 2. 虚函数 2.1 什么是虚函数 2.2 虚函数的重写 2.3 虚函数重写的两个…...

C#异常捕获

前言 在C#中&#xff0c;我们无法保证我们编写的程序没有一点bug&#xff0c;如果我们对于这些抛出异常的bug不进行任何的处理的话&#xff0c;那么我们的软件在抛出这些异常的时候就会崩溃&#xff0c;也就是软件闪退&#xff0c;并且这种闪退由于我们没有进行处理&#xff0…...

工业一体机根据软件应用需求灵活选配

在当今工业领域&#xff0c;数字化、智能化的发展趋势愈发明显&#xff0c;工业一体机作为关键的设备&#xff0c;其重要性日益凸显。而能够根据软件应用需求进行灵活选配的工业一体机&#xff0c;更是为企业提供了高效、定制化的解决方案。 一、工业一体机的全封闭无风扇散热功…...

centos7 mqtt服务mosquitto搭建记录

1、系统centos7.6&#xff0c;安装默认版本 yum install mosquitto 2、启动运行 systemctl start mosquitto 3、设置自启动 systemctl enable mosquitto 4、修改配置文件 vim /etc/mosquitto/mosquitto.conf 监听端口&#xff0c;默认为1883&#xff0c;需要修改删除前面…...

双阶段目标检测算法:精确与效率的博弈

双阶段目标检测算法&#xff1a;精确与效率的博弈 目标检测是计算机视觉领域的一个核心任务&#xff0c;它涉及在图像或视频中识别和定位多个对象。双阶段目标检测算法是一种特殊的目标检测方法&#xff0c;它通过两个阶段来提高检测的准确性。本文将详细介绍双阶段目标检测算…...

Python量化交易策略

策略详情 按照1分k线图&#xff1b;跳过9&#xff1a;30点1分k线图不计算 买入&#xff1b;监控市面的可转债&#xff1b;当某1分涨幅大于x涨幅&#xff0c;一直重复x次&#xff0c;选择买入&#xff0c;符合x设置的条件只选择成交额最大的可转债买入&#xff08;x要自定义&…...

为什么我感觉 C 语言在 Linux 下执行效率比 Windows 快得多?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;Windows的终端或者叫控制台…...

算法导论 总结索引 | 第四部分 第十六章:贪心算法

1、求解最优化问题的算法 通常需要经过一系列的步骤&#xff0c;在每个步骤都面临多种选择。对于许多最优化问题&#xff0c;使用动态规划算法求最优解有些杀鸡用牛刀了&#xff0c;可以使用更简单、更高效的算法 贪心算法&#xff08;greedy algorithm&#xff09;就是这样的算…...

用“文心一言”写的文章,看看AI写得怎么样?

​零售连锁店的“支付结算”业务设计 在数字化浪潮的推动下&#xff0c;连锁店零售支付结算的设计愈发重要。一个优秀的支付结算设计不仅能够提升用户体验&#xff0c;还能增强品牌竞争力&#xff0c;进而促进销售增长。 本文将围绕一个具体的连锁店零售支付结算案例&#xf…...

企业消费采购成本和员工体验如何实现“鱼和熊掌“的兼得?

有企业说企业消费采购成本和员工体验的关系好比是“鱼和熊掌”&#xff0c;无法兼得&#xff1f; 要想控制好成本就一定要加强管控&#xff0c;但是加强管控以后&#xff0c;就会很难让员工获得满意的体验度。如果不加以管控&#xff0c;员工自由度增加了&#xff0c;往往就很难…...

发表EI论文相当于SCI几区?

EI&#xff08;工程索引&#xff09;本身并不进行分区&#xff0c;它是一个收录工程领域高质量文献的数据库&#xff0c;与SCI&#xff08;科学引文索引&#xff09;的分区制度不同。然而&#xff0c;在非正式的学术评价中&#xff0c;有时人们会将EI与SCI的分区进行比较。 虽…...

STFT短时傅里叶变换MTLAB简析

代码&#xff1a; 解释&#xff1a; 如果信号x有Nx个时间样本&#xff0c;短时傅里叶变换的结果矩阵s有k列&#xff1b; k的计算方式如图所示&#xff0c;M是窗函数的长度&#xff0c;L是重叠长度。 此符号是向下取整符号。 短时傅里叶变换的结果矩阵s的行数与参数‘FFTLength’…...

海致科技实施实习生面试

一、面试内容 注&#xff1a;此次是电话面试 1.是XX先生吗 2.你是有考虑转实施的吗&#xff1f; 3.请讲一下你对项目部署实施的理解和掌握 4.用过数据库&#xff0c;会编写SQL语句吗&#xff1f; 5.讲一下SQL的常用关键字 6.了解SQL中的函数吗&#xff1f;谈谈函数 7.多…...

论文阅读之旋转目标检测ARC:《Adaptive Rotated Convolution for Rotated Object Detection》

论文link&#xff1a;link code&#xff1a;code ARC是一个改进的backbone&#xff0c;相比于ResNet&#xff0c;最后的几层有一些改变。 Introduction ARC自适应地旋转以调整每个输入的条件参数&#xff0c;其中旋转角度由路由函数以数据相关的方式预测。此外&#xff0c;还采…...

面向对象(Java)

构造方法只能在对象实例化的时候调用 this可以作为方法参数&#xff0c;表示调用方法的当前对象 this可以作为方法返回值&#xff0c;表示返回当前对象 封装 通过方法访问数据&#xff0c;隐藏类的实现细节 static&#xff1a;类对象共享&#xff0c;类加载时产生&#xff0c;…...

I/O多路复用

参考面试官&#xff1a;简单说一下阻塞IO、非阻塞IO、IO复用的区别 &#xff1f;_unix环境编程 阻塞io和非阻塞io-CSDN博客 同步阻塞(BIO) BIO 以流的方式处理数据 应用程序发起一个系统调用&#xff08;recvform&#xff09;&#xff0c;这个时候应用程序会一直阻塞下去&am…...

线性代数基础概念:向量空间

目录 线性代数基础概念&#xff1a;向量空间 1. 向量空间的定义 2. 向量空间的性质 3. 基底和维数 4. 子空间 5. 向量空间的例子 总结 线性代数基础概念&#xff1a;向量空间 向量空间是线性代数中最基本的概念之一&#xff0c;它为我们提供了一个抽象的框架&#xff0c…...

php 抓取淘宝商品评论数据 json

要抓取淘宝商品评论数据&#xff0c;你可以使用PHP的cURL库来发送HTTP请求并获取JSON格式的数据。 API接入流程&#xff1a;需要开放平台或者是封装接口注册账号&#xff0c;并申请相应的API使用权限&#xff0c;以获取必要的密钥和接口文档。获取接口使用权限&#xff1a;接入…...

Java 7新特性深度解析:提升效率与功能

文章目录 Java 7新特性深度解析&#xff1a;提升效率与功能一、Switch中添加对String类型的支持二、数字字面量的改进三、异常处理&#xff08;捕获多个异常&#xff09;四、增强泛型推断五、NIO2.0&#xff08;AIO&#xff09;新IO的支持六、SR292与InvokeDynamic七、Path接口…...

RHEL9找不到/var/log/dmesg日志文件问题

问题描述 在Rocky Linux 9 服务器上查看启动日志&#xff0c;发现没有/var/log/dmesg文件。 dmesg是什么&#xff1f; dmesg(diagnostic messages)用于打印kernel ring buffer的所有消息。 kernel会将开机信息存储在ring buffer中&#xff0c;如果开机时来不及查看启动信息&…...

是什么让以太坊从众多公链中脱颖而出

以太坊从众多公链中脱颖而出&#xff0c;成为区块链和加密货币领域的一个重要玩家&#xff0c;主要是由于以下几个关键因素&#xff1a; 智能合约&#xff1a; 以太坊是第一个广泛实施智能合约的区块链平台&#xff0c;智能合约允许在区块链上自动执行合同条款&#xff0c;无需…...

HarmonyOS--路由管理--组件导航 (Navigation)

文档中心 什么是组件导航 (Navigation) &#xff1f; 1、Navigation是路由容器组件&#xff0c;一般作为首页的根容器&#xff0c;包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式 2、Navigation组件适用于模块内和跨模块的路由切换&#xff0c;一次开发&#xff0…...

【Linux 命令】文件比较 diff

diff 命令是 Unix 和类 Unix 系统&#xff08;如 Linux 和 macOS&#xff09;中用于比较文件内容差异的一个非常有用的命令行工具。它可以逐行比较两个文件的内容&#xff0c;并输出它们之间的差异。这些差异通常以行为单位显示&#xff0c;并且会标记出哪些行是唯一的、添加的…...

猫头虎分享[可灵AI」官方推荐的驯服指南-V1.0

猫头虎分享[可灵AI」官方推荐的驯服指南-V1.0 猫头虎是谁&#xff1f; 大家好&#xff0c;我是 猫头虎&#xff0c;别名猫头虎博主&#xff0c;擅长的技术领域包括云原生、前端、后端、运维和AI。我的博客主要分享技术教程、bug解决思路、开发工具教程、前沿科技资讯、产品评…...

你的硬盘知道的太多:你以为你的秘密真的被删除了吗?

某一天你收到了朋友发给你的一个秘密文件&#xff0c;在看完之后&#xff0c;为了不被别人发现&#xff0c;你决定将文件毁尸灭迹&#xff01; 你选中文件名称 / 右键 / 删除&#xff0c;好了&#xff0c;文件已经消失了。但你是懂电脑的&#xff0c;知道文件此时还在回收站里面…...

虚拟机的网络配置

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️ 每一步都向着梦想靠近&#xff0c;坚持就是胜利的序曲 一 …...

ONLYOFFICE8.1版本桌面编辑器简单测评

ONLYOFFICE官网链接&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE介绍&#xff1a;https://www.onlyoffice.com/zh/office-suite.aspx OnlyOffice 是一款免费且开源的 Office 协作办公套件&#xff0c;支持桌面端和移动端等多平台&#xff0c;由一家领先的 IT 公…...

PDF内存如何变小,PDF内存压缩,PDF内存变小怎么调整

在数字化时代&#xff0c;pdf已成为工作、学习和生活中不可或缺的文件格式。它以其跨平台兼容性和安全性受到广大用户的喜爱。然而&#xff0c;随着pdf文件中嵌入的图片、图形和文本内容的增多&#xff0c;文件大小往往会变得相当可观&#xff0c;给文件的传输和存储带来一定的…...

深⼊理解MySQL Innodb存储引擎的缓冲池、事务、索引底层工作原理,掌握 MySQL 主从同步,读写分离技术以及集群的搭建,具备分库分表,SQL调优经验

深入理解MySQL的InnoDB存储引擎是数据库管理员和开发人员的重要技能。以下是对InnoDB存储引擎的缓冲池、事务、索引以及主从同步、读写分离技术和集群搭建的详细原理介绍&#xff1a; ### InnoDB存储引擎 1. **缓冲池(Buffer Pool)**&#xff1a; - 缓冲池是InnoDB存储引擎…...

《HelloGitHub》第 99 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、…...

mysql 将一个列按逗号分割为多列

在MySQL中&#xff0c;将一个列按逗号分割为多列通常需要使用字符串函数&#xff0c;如SUBSTRING_INDEX()&#xff0c;配合UNION ALL或CROSS JOIN等操作来实现。 假设有一个表my_table&#xff0c;它有一个列tags&#xff0c;其中存储了逗号分隔的标签值&#xff0c;如下所示&…...

Vue 3中 <script setup> 与生命周期钩子函数的详细解析

Vue 3中 <script setup> 与生命周期钩子函数的详细解析 Vue 3 引入了 <script setup> 语法糖&#xff0c;这是一种简化和集成组件逻辑的新方式。尽管 <script setup> 简化了组件的编写&#xff0c;但仍然可以利用 Vue 提供的生命周期钩子函数来管理组件的生…...

一篇文章入门主成分分析PCA

文章目录 基本概念事件随机变量独立同分布离散型随机变量伯努利分布&#xff08;两点分布&#xff09;二项分布几何分布泊松分布 连续型随机变量正态分布 期望方差标准化协方差相关系数线性组合特征值和特征向量特征值分解对称矩阵的特征值分解 齐次线性方程组单位向量基向量矩…...

Android系统为什么lmkd杀到adj 100就代表有低内存?

在Android系统中&#xff0c;lmkd&#xff08;Low Memory Killer Daemon&#xff0c;低内存终止守护进程&#xff09;负责监控系统的内存状态&#xff0c;并在内存压力较高时通过终止不必要的进程来释放内存&#xff0c;以维持系统的稳定运行。关于lmkd为何在杀到adj&#xff0…...

d嘤嘤不想求异或喵(牛客周赛49)

题意&#xff1a; 嘤嘤有两个整数 l,r&#xff0c;她想知道区间 [l,r] 所有整数的异或和是多少. 分析&#xff1a; 样例1只有一个数输出1 样例2 1^201^10113 样例3 1^2^301^10^1111^11000 #include<bits/stdc.h> using namespace std; typedef long long ll; ll f(l…...

java反射-动态调用方法

通过字符串动态创建对象&#xff0c;通过字符串动态使用对象方法 package com.hmdp.service.动态调用方法;import java.lang.reflect.Method;public class Main {public static void main(String[] args) throws Exception {String name "javax.swing.JFrame";Clas…...

ThreadLocal作用

ThreadLocal作用(线程本地存储) ThreadLocal&#xff0c;很多地方叫做线程本地变量&#xff0c;也有些地方叫做线程本地存储&#xff0c;ThreadLocal的作用是提供线程内的局部变量&#xff0c;这种变量在线程的生命周期内起作用&#xff0c;减少同一个线程内多个函数或者组件之…...

Python基础入门知识

目录 引言 简要介绍Python语言 为什么要学习Python Python的应用领域 Python安装和环境配置 Python的下载和安装(Windows, macOS, Linux) 配置Python环境变量 安装和使用IDE(如PyCharm, VS Code) Python基本语法 注释 变量和数据类型(数字,字符串,列表,元组,字典,…...

uniapp——据用户角色显示或隐藏部分功能权限。

v-if"user.state.agent_level!business || (user.state.agent_levelbusiness && item.value ! 3 && item.value ! 4)"...

JCR一区级 | Matlab实现BO-Transformer-LSTM多变量回归预测

JCR一区级 | Matlab实现BO-Transformer-LSTM多变量回归预测 目录 JCR一区级 | Matlab实现BO-Transformer-LSTM多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现BO-Transformer-LSTM多变量回归预测&#xff0c;贝叶斯优化Transformer结合LSTM长…...

软件开发环境-系统架构师(二十一)

1、对计算机评价的主要性能指标有时钟频率、&#xff08;&#xff09;、运算精度和内存容量等。 对数据库管理系统评价的主要性能指标有&#xff08;&#xff09;、数据库所允许索引数量和最大并发事务处理能力。 问题1 A丢包率 B端口吞吐量 C可移植性 D数据处理速率 问题…...

AI与大模型工程师证书研修班报名啦!

人工智能大模型是指拥有超大规模参数&#xff08;通常在十亿个以上&#xff09;、超强计算资源的机器学习模型&#xff0c;能够处理海量数据&#xff0c;完成各种复杂任务&#xff0c;如自然语言处理、图像识别等。计算机硬件性能不断提升&#xff0c;深度学习算法快速优化&…...

ctfshow-web入门-命令执行(web56、web57、web58)

目录 1、web56 2、web57 3、web58 1、web56 命令执行&#xff0c;需要严格的过滤 新增过滤数字&#xff0c;只能采用上一题临时文件上传的方法&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><…...

controller不同的后端路径对应vue前端传递数据发送请求的方式,vue请求参数 param 与data 如何对应后端参数

目录 案例一&#xff1a; 为什么使用post发送请求&#xff0c;参数依旧会被拼接带url上呢&#xff1f;这应该就是param 与data传参的区别。即param传参数参数会被拼接到url后&#xff0c;data会以请求体传递 补充&#xff1a;后端controller 参数上如果没写任何注解&#xff0c…...

【FFmpeg】avcodec_send_frame函数

目录 1.avcodec_send_frame1.1 将输入的frame存入内部buffer&#xff08;encode_send_frame_internal&#xff09;1.1.1 frame的引用函数&#xff08;av_frame_ref &#xff09;1.1.1.1 帧属性的拷贝&#xff08;frame_copy_props&#xff09;1.1.1.2 buffer的引用函数&#xf…...

python获取字符编码

在Python中&#xff0c;您可以使用内置的ord()函数获取单个字符的Unicode编码&#xff0c;使用encode()方法获取字符串的字节编码。 获取单个字符的Unicode编码: char a unicode_code ord(char) print(unicode_code) # 输出字符的Unicode编码 获取字符串的字节编码: tex…...

通过MATLAB控制TI毫米波雷达的工作状态之实时数据采集

前言 前一章博主介绍了如何基于MATLAB的各种前面板组件结合MATLAB代码来发送CFG指令控制毫米波雷达的工作状态,这一章节博主将介绍如何基于这些组件结合MATLAB代码来实现TI毫米波雷达数据的实时采集。目前大部分TI毫米波雷达的数据采集均是仅可以采集一段数据又或者利用DAC10…...

华为HCIP Datacom H12-821 卷21

1.单选题 以下关于PIM-SM中SPT切换的描述,错误的是哪一项? A、若所有组播流量都经过RP路由器,则RP路由器可能成为数据转发的瓶颈 B、SPT路径最短,转发性能更优 C、SPT 切换完成后,组播流量依然经过 ReT 树 D、RPT 树可能不是组播流量转发的最优路径 正确答案: C 解析…...

MySQL之应用层优化(二)

应用层优化 Web服务器问题 寻找最优并发度 每个Web服务器都有一个最佳并发度——就是说&#xff0c;让进程处理请求尽可能快&#xff0c;并且不超过系统负载的最优的并发连接数。这就是前面说的最大系统容量。进行一个简单的测量和建模&#xff0c;或者只是反复试验&#xf…...

Java源码解读之常量52429

文章目录 为什么有52429的常量呢&#xff1f;对于为什么选择52429?那么为什么不再选几位呢&#xff1f; 在JDK8源码中 java.lang.Integer有52429作为常量出现&#xff0c; 为什么有52429的常量呢&#xff1f; static void getChars(int i, int index, char[] buf) {int q, r;…...

【Unity 3D角色移动】

【Unity 3D角色移动】 在Unity 3D中实现角色移动通常涉及到几个关键步骤&#xff0c;包括设置角色的物理属性、处理输入、更新角色的位置以及动画同步。下面是实现基本3D角色移动的步骤和示例代码&#xff1a; 步骤1&#xff1a;设置角色的物理属性 角色通常使用Character Co…...

PhpStorm 2024 for Mac PHP集成开发工具

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…...

vue3使用pinia中的actions,需要调用接口的话

actions&#xff0c;需要调用接口的话&#xff0c;假如页面想要调用actions中的方法获取数据&#xff0c; 必须使用try catch async await 进行包裹&#xff0c;详情看下面代码 import {defineStore} from pinia import {reqCode,reqUserLogin} from ../../api/hospital/i…...

昇思25天学习打卡营第17天(+1)|Diffusion扩散模型

1. 学习内容复盘 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c;执行Python文件时&#xff0c;请确…...

MYSQL多个表进行笛卡尔积查询优化

如果A表和B表直接笛卡尔积进行条件查询 更改为A表条件查询&#xff0c;B表条件查询后再进行笛卡尔积进行查询 这样可以提高sql执行效率&#xff0c;说明原因如下 将直接进行笛卡尔积的查询优化为分别条件查询后再进行笛卡尔积可以显著提高SQL查询的执行效率。这种优化通常可以…...

联合概率密度函数

目录 1. 什么是概率密度由联合概率密度求概率参考链接 1. 什么是概率密度 概率密度到底在表达什么&#xff1f; 外卖在20-40分钟内送达的概率 随机变量落在[20,40]之间的概率。下图中&#xff0c;对总面积做规范化处理&#xff0c;令总面积1&#xff0c; f ( x ) f(x) f(x)则成…...

新车丨本田、吉利、丰田等,再添5台新车!颜值漂亮,下半年发布

今年上半年北京车展发布的一些新车,或将于今年下半年能上市,未买车的朋友先不要着急。以下5款新车或将在今年下半年上市,新车实力更强、颜值更高、续航更长,充电更快,此5款供大家借鉴。第一台本田发布的烨GT此车整体设计由中国研发团队自主设计符合国人审美,并且采用四门…...

年轻化的新一代迈腾,颜值并不丑,三大屏高通8155

年轻化的新一代迈腾,颜值并不丑,三大屏高通8155,像新势力中国汽车市场的竞争是越来越激烈,要想脱颖而出,即便是合资品牌旗下的主力常青树车型,现在也要拥抱变化了。就拿大众迈腾这款车来说,长期以来已经形成了一套固定的印象标签,无非就是中规中矩的沉稳商务风格。但全…...

再破记录!东风风神L7无补能2162.8公里往返穿越羌塘

想象一下驰骋在高速公路上,周围是辽阔的羌塘无人区,一望无际的荒野,野生动物自由穿梭,神秘而充满挑战。这条路就是传说中的216国道,无疑是最无人居住的地区道路。从新疆民丰到西藏改则,800公里无人区没有加油站,这是对车辆极限性能和团队冒险精神的双重考验!最近,在这…...

全球金融观察|“宽松交易”卷土重来,10年期美债收益率跌破4.4%关口

从种种迹象来看,美国经济似乎逐渐失去动能。在上周一季度GDP被下修后,美国再度公布了疲软的经济数据。当地时间6月3日,美国供应管理协会公布的数据显示,5月制造业PMI从4月的49.2降至48.7,连续二个月放缓,不及预期的49.6,新商品订单创下近两年来最大降幅,制造业物价支付…...

低代码开发难吗?

在软件开发的多样化浪潮中&#xff0c;低代码开发平台以其简化的编程模型&#xff0c;为IT行业带来了新的活力。作为一位资深的IT技术员&#xff0c;我对低代码开发平台的易用性和强大功能有着深刻的认识。今天&#xff0c;我将分享我对YDUIbuilder这一免费开源低代码平台的使用…...

CSS实现一个雨滴滑落效果

使用纯CSS来实现一个真实的雨滴滑落效果可能会有些挑战&#xff0c;因为CSS主要关注于静态样式和简单的动画效果。然而&#xff0c;你可以使用CSS动画和keyframes来模拟一个雨滴滑落的简化效果。 以下是一个基本的示例&#xff0c;展示如何使用CSS来模拟雨滴从顶部滑落到底部的…...