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

【C++】多态的实现及其底层原理

在这里插入图片描述
在这里插入图片描述

个人主页:🍝在肯德基吃麻辣烫

我的gitee:gitee仓库
分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处。


文章目录

  • 前言
  • 一、什么是多态?
  • 二、多态的构成条件
    • 2.1什么是虚函数?
    • 2.2虚函数的重写
    • 2.3 什么是虚函数表?
      • 有虚函数的对象的大小
    • 2.4普通对象调用和实现多态后的对象调用
  • 三、多态的原理
      • 经典题
      • 回到多态的两个条件
    • 多态条件的两个特例:(这一点是C++的大坑)
    • 3.1动态绑定和静态绑定
  • 四、默认成员函数和虚函数的关系
    • 4.1构造函数可以设置成虚函数吗?
    • 4.2 析构函数可以设置成虚函数吗?
    • 一道经典面试题
  • 五、单继承和多继承关系中的虚函数表
    • 5.1单继承关系中的虚函数表
    • 5.2多继承关系中的虚函数表
  • 六、抽象类
  • 写在最后的面试题
  • 总结


前言

本文继C++继承之后讲解C++多态。


一、什么是多态?

单单从概念入手不好理解,应该深入理解多态的实现后再回过头来讲解。
现在简单举个例子:我们在购买高铁票时,往往会有成人票全价,学生票半价的优惠,针对不同的人群给予不同的优惠,这个就是多态(多种形态)。

二、多态的构成条件

多态的两个构成条件为:

1.基类的指针或引用
2.满足虚函数的重写

2.1什么是虚函数?

virtual关键字修饰的类成员函数就是虚函数。

在继承中,子类要想重写父类,父类的成员函数必须是虚函数,而子类的成员函数可以不加virtual,但一般建议加上比较合适。

2.2虚函数的重写

虚函数的重写是:对父类的虚函数的实现进行覆盖,覆盖的内容是子类虚函数的实现。

满足重写的条件:三同。

函数名,参数类型,返回值相同的虚函数,就能满足重写的条件。

2.3 什么是虚函数表?

虚函数表是继承体系中,如果一个函数是虚函数,则该函数的地址会存储在一张虚函数表中,而不是存储在对象中,该虚函数表的地址才存储在对象中。通过虚函数表可以找到对应的虚函数,从而能够进一步实现多态。
在这里插入图片描述

有虚函数的对象的大小

class A
{
public:virtual void func1(){}virtual void func2(){}protected:int _a;
};int main()
{cout << sizeof(A) << endl;return 0;
}

请计算上面的代码中,A这个类的大小。
A有两个虚函数,一个成员,实际上大小为8

原因:
虚函数在内存中是存储在虚函数表中,而不是存在类对象中,类对象在内存中存储的是一个虚函数表指针和成员变量。
指针大小是4字节,成员是int类型,则共为8字节。
在这里插入图片描述

虚函数表本质上是一个函数指针数组。
一般建议:如果不实现多态,就不要设置成虚函数

2.4普通对象调用和实现多态后的对象调用

普通对象调用成员函数是在编译期间就确定了地址,而实现多态后的函数是在运行期间才确定地址。因为子类继承父类,子类先拷贝父类的虚函数表的地址,如果某个函数是虚函数,则会在子类的虚函数表中重写改函数的地址。

所以编译器在遇到父类的指针或引用调用子类的函数时,编译器在编译期间无法确定到底调用谁的函数,只能运行起来去子类的虚函数表中查找,是谁的地址就调用谁。

三、多态的原理

我们通过以下例子来看待多态。

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

首先:子类继承了父类,父类在内存中有一张虚表,存的是虚函数的地址。当子类继承了父类后,子类中也同样有一张虚表,只不过是这张虚表上的虚函数的地址是拷贝父类的,如果子类中有虚函数,且满足重写的条件,则子类的虚函数表的虚函数的地址会被重写覆盖成子类的虚函数的地址。

在这里插入图片描述

当我们用父类的指针或引用调用子类的虚函数时,会去访问子类的虚函数表,当我们调用父类的虚函数时,会访问父类的虚函数表,这样就实现了父类指针指向父类对象就调用父类的虚函数表,指向子类对象就调用子类的虚函数表。在这里插入图片描述

所以多态就是:我们想调用父类的函数就传递父类对象给父类指针/引用,想调用子类的函数就传递子类对象给父类指针/引用。

经典题

这里有一道非常经典的坑人题目:

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

问:结果输出什么?
对于一个子类指针p指向子类对象,通过这个指针调用函数test,因为子类继承了父类,父类就成了子类的成员,通过父类的虚表找到test函数的地址调用,在test函数中又调用func函数,因为这是B的指针,调用func函数时会去B对象的虚表中找到func的地址,这个地址不再是从父类那里拷贝的func的地址,这个地址已经被重写成子类的func的地址,所以调用的是子类的func函数。
然而,虚函数的重写只是重写函数的实现,函数的接口仍然继承父类的,这个叫做接口继承,所以参数的缺省值仍然是父类的。
结果输出B->1

回到多态的两个条件

多态的两个条件:

1.父类的指针或引用
2.虚函数的重写

(1)为什么不能是子类的对象赋值给父类的对象,而是子类对象赋值给父类的指针/引用?
因为子类对象赋值给父类对象,切片过程中不会拷贝虚表。所以父类对象只能调用父类的虚表,子类对象才能调用子类的虚表,满足不了多态。

(2)为什么子类的对象赋值给父类的对象不会拷贝虚表?
因为如果拷贝虚表,使用子类对象会调用子类的虚函数,使用父类对象也会拷贝子类的虚函数,就乱套了。

(3)为什么不能是子类的指针或引用?因为父类是子类的一部分,父类赋值给子类指针不会切片,就不能获取父类的虚函数表。

需要注意的以下几点:

(1)虚函数存在哪?虚函数表存在哪?
注意:虚函数和普通函数一样,都是存在代码段的,虚函数表也是存在代码段的。
是虚函数地址存在虚表中,虚函数表的地址存在对象中。
我们可以通过打印地址的方式验证一下虚函数表存在代码段:

void test_where()
{Person p;Student s;//栈int a;printf("栈->[%p]\n", &a);//堆区int* ptr = new int;printf("堆->[%p]\n", ptr);//数据段(静态区)static int b;printf("静态->[%p]\n", &b);//代码段(常量区)const char* str = "Hello World";printf("常量区->[%p]\n", str);printf("虚函数表1->[%p]\n", *((int*)&p));printf("虚函数表2->[%p]\n", *((int*)&s));}int main()
{test_where();return 0;
}

思路:获取父类或子类对象的虚函数表指针,也就是取出父类/子类在内存中的前4个字节,将该地址与内存中的栈区,堆区,静态区(数据段),常量区(代码段)的地址进行对比,跟谁的地址比较近,就大致在哪个区域。

(2)子类继承父类后,如果子类也有自己的虚函数,则这些虚函数的地址是按照他们声明的顺序依次存在虚表的最后,因为前面先存父类的虚函数的地址。

多态条件的两个特例:(这一点是C++的大坑)

(1)协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
(这一点是C++的大坑)

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

3.1动态绑定和静态绑定

多态可以分为动态多态和静态多态,动态多态也叫做动态绑定,静态多态也叫做静态绑定。

静态绑定有:函数重载
函数重载在编译时进行匹配,cout的自动识别类型的底层也是使用函数重载实现的。
函数重载原理:函数名修饰规则
动态绑定:多态。
编译阶段编译器不知道到底调用子类的虚函数还是父类的虚函数,所以编译器只能通过给定的对象,在运行期间,通过调用虚函数表的地址来进行查看。这个过程叫做动态绑定。
多态原理:虚函数表

四、默认成员函数和虚函数的关系

4.1构造函数可以设置成虚函数吗?

构造函数不能是虚函数,因为虚函数表是在构造函数的初始化列表初始化之后才会生成的。如果构造函数是虚函数,那么调用构造函数的时候要去虚函数表里面找到构造函数的地址来进行调用。那么如何找到虚表呢?这就是一个典型的现有鸡还是先有蛋的问题了。

4.2 析构函数可以设置成虚函数吗?

析构函数可以设置成虚函数,并且强烈建议设置成虚函数。

(1)析构函数加virtual,是不是虚函数?
是,因为在编译器编译阶段,会通过函数名修饰规则,统一将子类的析构函数和父类的析构函数改成:destructor。
(2)为什么要这么处理?

因为在子类析构函数调用完成后必须要调用父类的析构函数对父类的资源释放。设置成虚函数可以完成子类对父类的析构函数的重写。
(3)为什么要让他们构成重写?
因为在下面的场景中,构成重写可以实现父类指针调用子类对象会调用子类的析构,子类析构调用结束会自动调用父类的析构,父类指针调用父类对象会调用父类的析构。从而实现不同对象传递给父类指针/引用,会调用不同对象的函数,实现多态。

如果不设置成虚函数,假如有以下的场景,会出问题:

class Person
{
public:~Person(){cout << "father:~Person" << endl;}
};class Student : public Person
{
public:~Student(){cout << "son:~Student" << endl;delete [] ptr;}protected:int* ptr = new int[10];//new ==> 构造 + operator new()
};int main()
{Person* p = new Person;delete p;p = new Student;delete p;return 0;
}

delete p 会处理成以下方式:
p->destructor() + operator delete( p )

普通对象,看当前者的类型。
多态对象,看指向对象的类型。

p是父类对象,不管指向的对象是父类还是子类,都会调用父类的析构函数,在子类申请的空间就得不到释放,会造成内存泄露问题。
如果设置成虚函数,就能够实现虚函数的重写,从而实现多态。

一道经典面试题

设计一个不想被继承的类,如何创建?
(1)构造函数设置成私有

class A
{
private:A(){}};class B : public A
{
public:B(){}};

原理:在继承体系中,子类的构造函数必须先去调用父类的构造函数,这里父类构造设置成私有子类就无法调用了。

不过这里出现一个问题,子类无法调用父类的构造,父类也无法调用自己的构造了。
我们可以写一个函数,在函数里面创建一个父类。

class A
{
private:A Createobj(){return A();}A(){}};

但是又有一个问题:如何调用这个函数呢?因为调用该函数创建对象,而创建对象又需要在函数里面创建。
这里我们可以加一个static解决

class A
{
private:static A Createobj(){return A();}A(){}};int main()
{A a = A::Createobj();
}

这样就可以通过指定类域访问该函数解决。

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

5.1单继承关系中的虚函数表

单继承关系中,子类会拷贝父类的虚函数表,如果子类还有自己的虚函数,则该虚函数的地址会放在虚函数表的最后。

我们通过调试窗口无法看到子类的虚函数。在这里插入图片描述
不过我们可以通过获取虚表的地址来打印虚表的各个虚函数的地址。

typedef void(*VFPTR)();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; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b = 2;
};void PrintVfptr(VFPTR* arr)
{printf("虚表地址是:[%p]\n", arr);for (int i = 0; arr[i] != nullptr; i++){printf("第%d个虚函数地址是:[%p]\n", i, arr[i]);}printf("\n");}int main()
{Base b;Derive d;int vfptrb = *((int*)&b);int vfptrd = *((int*)&d);PrintVfptr((VFPTR*)vfptrb);PrintVfptr((VFPTR*)vfptrd);return 0;
}
  • 思路:1.先获取虚表的地址,取子类对象的地址,强转成int*,再进行解引用,就取到了子类对象的前4个字节,也就是虚表指针。
  • 2.再强转成(VFPTR*)通过打印该指针指向的内容,即可打印虚表的内容。

5.2多继承关系中的虚函数表

这里有几种猜测,子类的未重写的虚函数会放在第一个继承的父类的虚表中,或者放在其他的父类的虚表中。

我们也可以通过打印地址的方式确定。

typedef void(*VFPTR)();class Base1 
{
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }private:int b1 = 1;
};class Base2 
{
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Derive::func2" << endl; }
private:int b2 = 2;
};class Derive1 :public Base1, public Base2
{
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d = 2;
};void PrintVfptr(VFPTR* arr)
{printf("虚表地址是:[%p]\n", arr);for (int i = 0; arr[i] != nullptr; i++){printf("第%d个虚函数地址是:[%p]\n", i, arr[i]);}printf("\n");}int main()
{Derive1 d;int vfptrb1 = *((int*)&d);Base2* p2 = &d;//自动切片,p2就指向Base2对象的首地址int vfptrb2 = *(int*)p2;//int vfptrb2 = *((int*)((char*)&d + sizeof(Base1)));PrintVfptr((VFPTR*)vfptrb1);PrintVfptr((VFPTR*)vfptrb2);return 0;
}

在这里插入图片描述

通过打印可以看到,子类中未重写的虚函数放在第一个继承的父类的虚表中。

但是这里有一个问题:为什么重写了func1,在Base1的func1的地址和Base2的func1的地址不一样?
在这里插入图片描述

在ptr1和ptr2调用func1的过程中,调用的是Derive的func1函数,因为func1已经被重写了。

而在内存中,ptr1和ptr2指向的地址如下:

在这里插入图片描述

因为ptr1指向的地址刚好是d对象的首地址,ptr1和this指针是重叠的,无需偏移,而ptr2需要偏移Base1字节才与this指针重叠。
这就导致在调用func1函数前,ptr2需要先偏移,才能调用。我们看到的是在偏移之前的ptr2的地址,这就是为什么看到的func1的地址不同,调用的确实同一个函数的原因,实际上ptr2会偏移。

六、抽象类

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

class
{
public:virtual void func1() = 0{}	
};

比如上面这个就是抽象类。
抽象类的特性:

  • 1.不能实例化出对象,子类继承后也不能实例化出对象,只有重写虚函数才能实例化出对象。
  • 2.可以定义指针或引用.

普通的对象是实现继承,而实现多态的对象是接口继承。

抽象类的作用:间接强制子类虚函数必须重写,否则无法实例化对象。

写在最后的面试题

  1. 什么是多态?
  2. 什么是重载、重写(覆盖)、重定义(隐藏)?
  3. 多态的实现原理?
  4. inline函数可以是虚函数吗?
  5. 静态成员可以是虚函数吗?
  6. 构造函数可以是虚函数吗?
  7. 析构函数可以是虚函数吗?
  8. 对象访问普通函数快还是虚函数更快?
  9. 虚函数表是在什么阶段生成的,存在哪的?
  10. C++菱形继承的问题?虚继承的原理?
  11. 什么是抽象类?抽象类的作用?

1.多态分为静态多态和动态多态。
静态多态是函数重载,本质上是函数名修饰规则来完成。
动态多态:继承中的虚函数重写 + 父类指针或引用两个条件完成。
动态多态的本质上是由虚函数表的实现来完成。
2.重载:两个函数必须在同一作用域,函数名和参数类型必须相同。
重写(覆盖):两个函数在父类和子类的作用域,且要求满足函数名,参数类型,返回值必须相同(协变例外),两个函数必须是虚函数。
重定义(隐藏)两个函数在父类和子类的作用域,且要求满足函数名相同,两个父类和子类的重名函数不构成重写就一定是重定义。
3.多态实现的原理:
静态多态:函数名修饰规则
动态多态:虚函数表
4.不可以,因为一个函数如果设置成内联,就是一段代码,没有产生地址,无法将地址放进虚表,就不能是虚函数了。(声明:如果一个内联函数被virtual修饰,那么该函数就会自动忽略inline的属性,而成为一个虚函数。
5. 静态成员函数不能是虚函数,因为静态成员函数没有this指针,使用类型::成员函数的方式来调用该函数,但这种方式无法访问虚函数表,所以静态成员函数无法放进虚表里,无法实现多态,就没有意义。
6.构造函数不能是虚函数,构造函数不能是虚函数,因为虚函数表是在构造函数的初始化列表中生成的,如果构造函数是虚函数,又得取到虚函数表里面找到构造函数的地址,所以是现有构造函数还是先有虚表呢?
7.析构函数可以是虚函数,并且强烈建议析构函数设置成虚函数。因为父类和子类的析构函数会被编译器统一编译成destructor,有些特殊场景必须实现多态才能解决,请参考本文内容。
8.对象访问普通函数和虚函数是一样快的,因为它们都是在编译期间就确定。但如果是指针对象或引用对象调用,是普通函数快,因为调用虚函数会去到虚表中查找地址,速度会慢一些。
9.虚函数表在编译阶段生成的,存在代码段,具体参考本文章的案例。
10.菱形继承的问题:数据冗余和二义性。菱形虚拟继承原理:生成一张虚基表,存放的是该对象相对于父类对象的偏移量,通过偏移量可以访问父类的成员,不再需要在每个子类中存一份父类的成员。
11.抽象类:具有纯虚函数的类叫做抽象类。而在虚函数后面加上 = 0,就是纯虚函数。抽象类的意义:强制子类进行虚函数的重写,并且抽象类体现了接口继承关系。

总结

多态内容就讲到这里。

相关文章:

【C++】多态的实现及其底层原理

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 我的gitee&#xff1a;gitee仓库 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、什么是多态&#xff1f;二、多态的构成条件2.1什么是虚函数&#xff1f;2.2虚函数的重写2…...

【网络编程】TCP带外数据总结

文章目录 一、带外数据基本知识二、带外数据的读写三、检测带外数据是否到达3.1、select上的异常事件3.2、SIGURG信号 四、带外标记 一、带外数据基本知识 带外数据&#xff08;Out Of Band&#xff0c;OOB&#xff09;&#xff0c;用于迅速通告对方本端发生的重要事件&#xf…...

高薪程序员面试题精讲系列133之微服务里的网关有哪些实现方案?你熟悉Gateway网关吗?

一. 面试题及剖析 1. 今日面试题 微服务里的网关有哪些实现方案? Gateway网关是怎么实现的? 你用过Gateway网关吗? Gateway里有哪些路由规则? 2. 题目剖析 在上一篇文章中,壹哥给大家梳理了微服务里的远程调用、熔断等相关的面试题。今天这篇文章,壹哥会重点给大家梳理…...

计算机网络(4) --- 协议定制

计算机网络&#xff08;3&#xff09; --- 网络套接字TCP_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/132035757?spm1001.2014.3001.5501 目录 1. 协议的基础知识 TCP协议通讯流程 ​编辑 2.协议 1.介绍 2.手写协议 1.内容 2.接口 …...

【Mybatis】Mybatis架构简介

文章目录 1.整体架构图2. 基础支撑层2.1 类型转换模块2.2 日志模块2.3 反射工具模块2.4 Binding 模块2.5 数据源模块2.6缓存模块2.7 解析器模块2.8 事务管理模块 3. 核心处理层3.1 配置解析3.2 SQL 解析与 scripting 模块3.3 SQL 执行3.4 插件 4. 接口层 1.整体架构图 MyBatis…...

如何使用大模型处理生活繁琐的工作

如果每封电子邮件、每个带有订单、发票、投诉、录用请求或工作申请的 PDF 都可以翻译成机器可读的数据&#xff0c;会怎样&#xff1f;然后可以由 ERP / CRM / LMS / TMS 自动处理吗&#xff1f;无需编程特殊接口。 听起来很神奇&#xff1f;它确实有一些魔力。但最近已成为可…...

RpcController作用浅析

RpcController作用浅析 前面提到了RpcConsumer的实现思路&#xff0c;但是并没说明RpcController有什么作用&#xff0c;不妨看看google::protobuf::RpcController&#xff1a; class PROTOBUF_EXPORT RpcController {public:inline RpcController() {}virtual ~RpcControlle…...

Linux(三):Linux服务器下日常实操命令 (常年更新)

基础命令 cd命令&#xff1a;切换目录 cd &#xff1a;切换当前目录百至其它目录&#xff0c;比如进入/etc目录&#xff0c;则执行 cd /etccd / &#xff1a;在Linux 系统中斜杠“/”表示的是根目录。cd / ,即进入根目录.cd ~&#xff1a;进入用户在该系统的home目录&#…...

强大的截图软件--Snipaste

这里写目录标题 前言Snipaste贴图并置顶标注功能 下载 前言 在工作中&#xff0c;我们经常需要保存当前屏幕的图片&#xff0c;虽然系统总是会自带一些截图工具&#xff0c;但似乎用起来总是不那个顺手&#xff0c;例如我们需要对图片进行一些标注&#xff0c;或者将图片贴在屏…...

LeetCode·每日一题·722. 删除注释·模拟

题目 示例 思路 题意 -> 给定一段代码&#xff0c;将代码中的注释删除并返回。 由于注释只有两种类型&#xff1a; 字符串// 表示行注释&#xff0c;表示//和其右侧的其余字符应该被忽略。字符串/* 表示一个块注释&#xff0c;它表示直到下一个&#xff08;非重叠&#x…...

npm更新和管理已发布的包

目录 1、更改包的可见性 1.1 将公共包设为私有 ​编辑 使用网站 使用命令行 1.2 将私有包公开 使用网站 使用命令行 2、将协作者添加到用户帐户拥有的私有包 2.1 授予对Web上私有用户包的访问权限 2.2 从命令行界面授予私有包访问权限 2.3 授予对私有组织包的访问权限…...

如何高效使用Gherkin

背景 时间回到2022年&#xff0c;我参与了一个使用了Flutter技术构建的Web前端项目。在这个项目上&#xff0c;我们小组的目标是实施Flutter前端自动化测试。 彼时&#xff0c;Flutter 2.x刚在Web端发力不久&#xff0c;Flutter Web上的应用和生态才刚刚开始&#xff0c;而在…...

[CKA]考试之调度 pod 到指定节点

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 创建一个Pod&#xff0c;名字为nginx-kusc00401&#xff0c;镜像地址是nginx…...

git 常用命令有哪些

Git 是我们开发工作中使用频率极高的工具&#xff0c;下面总结下他的基本指令有哪些&#xff0c;顺便温习一下。 前言 一般项目中长存2个分支&#xff1a; 主分支&#xff08;master&#xff09; 和开发分支&#xff08;develop&#xff09; 项目存在三种短期分支 &#xff1…...

算法leetcode|66. 加一(rust重拳出击)

文章目录 66. 加一&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 66. 加一&#xff1a; 给定一个由 整数 组成的 非…...

MySQL备份Shell脚本

将此脚本添加到crontab计划中&#xff0c;自动留存最新的两份备份 #!/bin/bash # 数据库配置 DB_HOST"localhost" DB_USER"root" DB_PASS"Sxbdc123!#" DB_NAME"ww"# 备份目录 BACKUP_DIR"/opt/mysqlbak"# 备份文件名称 BA…...

Python批量查字典和爬取双语例句

最近&#xff0c;有网友反映&#xff0c;我的批量查字典工具换到其它的网站就不好用了。对此&#xff0c;我想说的是&#xff0c;互联网包罗万象&#xff0c;网站的各种设置也有所不同&#xff0c;并不是所有的在线字典都可以用Python爬取的。事实上&#xff0c;很多网站为了防…...

uni-app、H5实现瀑布流效果封装,列可以自定义

文章目录 前言一、效果二、使用代码三、核心代码总结前言 最近做项目需要实现uni-app、H5实现瀑布流效果封装,网上搜索有很多的例子,但是代码都是不够完整的,下面来封装一个uni-app、H5都能用的代码。在小程序中,一个个item渲染可能出现问题,也通过加锁来解决问题。 一、…...

vue echart3个饼图

概览&#xff1a;根据UI设计需要做3个饼图且之间有关联&#xff0c;并且处理后端返回的数据。 参考链接&#xff1a; echart 官网的一个案例&#xff0c;3个饼图 实现思路&#xff1a; 根据案例&#xff0c;把数据处理成对应的。 参考代码&#xff1a; 1.处理后端数据&am…...

LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 论文阅读

论文信息 题目&#xff1a;LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 作者&#xff1a;Devendra Singh Chaplot, Dhiraj Gandhi 项目地址&#xff1a;https://devendrachaplot.github.io/projects/Neural-SLAM 代码地址&#xff1a;https://github.com/devendrachaplot/N…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)

本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢&#xff0c;连接红外测温传感器&#xff0c;可实时精准捕捉宠物体温变化&#xff0c;以便及时发现健康异常&#xff1b;水位检测传感器时刻监测饮用水余量&#xff0c;防止宠物…...