C++ 之多态虚函数原理及应用

文章目录

    • 多态基本概念和原理
    • 虚函数的基本原理和概念
    • 虚析构和纯虚析构
    • 多重继承中的虚函数
    • 小结

多态基本概念和原理

多态的基本概念

**多态是C++面向对象三大特性之一**

多态的定义

多态是一种面向对象编程概念,指同一个行为(方法)在不同的对象上有不同的实现方式。其定义可以简单地理解为“同名多形”,即同一个方法名可以被不同的类实现,并且表现出不同的行为。 多态使得程序具有更高的灵活性和可扩展性,因为它使得代码与具体对象的实现细节分离开来,从而可以方便地添加新的类或修改旧的类的实现方式,同时也使得代码更易于维护。

虚函数和多态的关系

虚函数是实现多态的关键。在基类中声明一个虚函数,当子类重写该函数时,调用该函数将根据对象类型动态确定。这样做可以实现运行时多态,即在程序运行期间根据对象类型动态确定哪个函数应该被调用。这使得代码更加灵活和可扩展,并且减少了大量的条件判断语句。因此,虚函数和多态密不可分。

多态分为两类

  • 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态: 派生类和虚函数实现运行时多态

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定  -  编译阶段确定函数地址
  • 动态多态的函数地址晚绑定  -  运行阶段确定函数地址

    动态多态满足条件
    1、有继承关系
    2、子类重写父类的虚函数

    动态多态的使用
    父类的指针或者引用指向子类的对象

   重写:函数返回值类型  函数名 参数列表 完全一致称为重写

#include <iostream>
using namespace std;

	class Animal
	{
	public:
	//speak函数就是虚函数
	//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。

		virtual void speak() { //类Animal的成员函数speak前不加virtual,其size是1,加virtual其size是4,增加一个虚函数指针
			cout << "动物在说话" << endl;
		}

	};
	class Cat :public Animal {
	public:
		//子类重写父类的虚函数,void前加不加virtual均可
		//重写:函数返回值类型  函数名 参数列表 完全一致称为重写
		void speak() {
			cout << "小猫在说话" << endl;
		}
	};

	//执行说话的函数
	//地址早绑定,在编译阶段确定函数地址,父类的speak函数前不加virtual,则即使DoSpeak传入参数cat,则也是调用的父类的speak函数
	//如果想让小猫说话,那么这个函数地址就不能早绑定,需要在运行阶段进行绑定,地址晚绑定

	//我们希望传入什么对象,那么就调用什么对象的函数
	//如果函数地址在编译阶段就能确定,那么静态联编
	//如果函数地址在运行阶段才能确定,就是动态联编

	//动态多态满足条件
	//1、有继承关系
	//2、子类重写父类的虚函数

	//动态多态的使用
	//父类的指针或者引用指向子类的对象

	void DoSpeak(Animal &animal)  //父类的指针或者引用指向子类的对象 Animal &animal = cat;
	{
		animal.speak();
	}

	void test() {
		Cat cat;
		DoSpeak(cat);

	}
int main(){
	test();
	return 0;
}

多态的基本原理

实现原理:虚函数表+虚表指针

编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr),这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针。

举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。看下面两种情况:

如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。

如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。

调用虚函数时,程序将查看存储在对象中的虚函数表地址,转向相应的虚函数表,使用类声明中定义的第几个虚函数,程序就使用数组的第几个函数地址,并执行该函数。

//当父类的指针或者引用指向子类的对象时,发生多态
Animal &animal = cat;  //由于此处指向的是cat对象,所以它会从cat对象的虚函数表中去找speak()函数,在运行阶段发生了动态多态,你传的是cat对象,它就从cat对象的虚函数表中地址找该函数,最后实现调用cat的speak()函数
animal.speak();

类Animal的成员函数speak前不加virtual,其size是1,加virtual其size是4,增加一个虚函数指针

当子类没有重写父类的虚函数时

子类重写父类的虚函数时,子类的虚函数指针指向的地址,将子类虚函数地址将父类的函数地址覆盖了,动态多态实现地址的晚绑定。

多态案例-计算器类

//利用多态实现计算机类

//多态的好处
//1、组织结构清晰
//2、可读性强
//3、对于前期和后期的扩展和维护性高


#include <iostream>
#include <string>
using namespace std;

class Abcal {
public:
  virtual int getResult() { return 0; }
  int num1;
  int num2;
};

//设计加法计算器类
class Addcal : public Abcal {
public:
  int getResult() { return num1 + num2; }
};

//减法的计算器类
class Subcal : public Abcal {
public:
  int getResult() { return num1 - num2; }
};

//乘法的计算器类
class Mulcal : public Abcal {
public:
  int getResult() { return num1 * num2; }
};

void test() {
  //多态的使用
  //父类的指针或者引用指向子类的对象

  //加法计算
  Abcal *a = new Addcal; // new Addcal 创建加法计算器的对象  //堆区
  a->num1 = 10;
  a->num2 = 10;
  int c = a->getResult();
  cout << "c = " << c << endl;

  //堆区手动开辟数据需要手动释放
  delete a; //只释放堆区的数据,指针的类型没有变还是父类的指针

  //减法计算
  a = new Subcal;
  a->num1 = 10;
  a->num2 = 10;
  int d = a->getResult();
  cout << "d = " << d << endl;

  delete a;
}
int main() {
  test();
  return 0;
}


虚函数的基本原理和概念

一、什么是虚函数?
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数

用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

二、虚函数定义
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。下面来看一段简单的代码。

三、虚函数的作用

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数(动态绑定)。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

虚函数可以让子类对象在运行时动态地继承和修改父类的成员函数,使得代码更加灵活、可重用,并且可以实现多态性和抽象类等高级特性。

  1. 通过虚函数,可以实现多态性(Polymorphism),即同一个函数名可以在不同的子类中表现出不同的行为,这样可以提高代码的可重用性和灵活性。
  2. 避免静态绑定,在使用父类指针或引用调用子类对象的成员函数时,如果没有使用虚函数,则会进行静态绑定(Static Binding),即只能调用父类的成员函数,无法调用子类特有的成员函数。
  3. 虚函数的调用是动态绑定(Dynamic Binding)的,即在运行时根据指针或引用所指向的对象类型来选择调用哪个函数,从而实现动态多态性。
  4. 抽象类是一种不能直接实例化的类,只能被其他类继承并实现其虚函数。通过定义纯虚函数(Pure Virtual Function),可以使得一个类成为抽象类,强制其子类必须实现该函数。

四、虚函数的底层实现原理

实现原理:虚函数表+虚表指针

  • 虚函数表:每个包含虚函数的类都会生成一个虚函数表(Virtual Table),其中存储着该类中所有虚函数的地址。虚函数表是一个由指针构成的数组,每个指针指向一个虚函数的实现代码。
  • 虚函数指针:在对象的内存布局中,编译器会添加一个额外的指针,称为虚函数指针或虚表指针(Virtual Table Pointer,简称 VTable 指针)。这个指针指向该对象对应的虚函数表,从而让程序能够动态地调用正确的虚函数。

当一个基类指针或引用调用虚函数时,编译器会使用虚表指针来查找该对象对应的虚函数表,并根据函数在虚函数表中的位置来调用正确的虚函数。

1、虚函数表的概念与作用

虚函数表是一种用于实现多态的机制,在 C++ 中,当一个类中包含虚函数时,编译器会自动为该类生成一个虚函数表。这个表由包含虚函数的类的指针所指向,其中每个条目都存储着对应虚函数的地址。当使用一个对象调用其虚函数时,编译器通过该对象的虚函数表找到对应的虚函数地址,并进行调用。

虚函数表的作用在于实现了多态,即通过基类指针或引用调用派生类的虚函数,实现了不同对象的不同行为。因此,虚函数表使得程序可以更加灵活地处理不同类型的对象,并支持代码的扩展和修改。同时,由于虚函数表的存在,C++ 中的虚函数调用比起其他语言(如 Java)中的成本要高,因为需要额外的内存空间存储虚函数表及其指针。

2、虚函数指针的意义

虚函数指针的意义在于实现多态。多态是指同一种操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。通过使用虚函数和虚函数指针,可以在运行时确定调用哪个子类中的虚函数,从而实现了动态绑定,使得程序具有更好的灵活性和扩展性。

此外,虚函数指针也是实现多级继承的关键,在多级继承中,每个子类都需要维护自己的虚函数表及其对应的虚函数指针。

3、虚函数的调用过程

  1. 在编译期间,编译器会根据函数调用的类型和对象的类型确定要调用的函数。
  2. 在运行期间,程序会根据对象的实际类型来决定调用哪个函数。这个过程叫做动态绑定或者后期绑定。
  3. 程序通过虚函数表(vtable)来实现动态绑定。每个含有虚函数的类都有自己的虚函数表,存储了指向实际函数地址的指针。在对象被创建时,它的指针会指向所属类的虚函数表。
  4. 当调用虚函数时,在对象中存储的指针会被解引用,获取到虚函数表的地址。然后根据函数调用的类型,从虚函数表中获取相应的函数地址。
  5. 最后,程序跳转到函数地址处执行实际的代码。由于是动态绑定,所以调用的函数是根据对象实际类型来决定的。

4、虚函数的重写与覆盖

在 C++ 中,派生类可以重写 (override) 它继承的虚函数,这被称为函数的覆盖 (overriding)。当然,子类也可以选择不重写基类的虚函数,那么它将默认继承基类的实现,这就是虚函数的重载 (overloading)。

虚函数的重写了解起来比较简单,子类定义与父类相同名称和参数列表的虚函数,此时会自动调用子类中定义的方法,而不会调用父类的同名虚函数。例如:

class Base {
public:
    virtual void foo() {
        std::cout << "Base::foo()" << std::endl;
    }
};

class Derived : public Base {
public:
    void foo() {
        std::cout << "Derived::foo()" << std::endl;
    }
};

int main() {
    Derived obj;
    Base* ptr = &obj;
    ptr->foo(); // 输出:Derived::foo()
    return 0;
}

例子中,派生类 Derived 重写了基类 Base 的虚函数 foo(),所以在调用指针 ptr 的 foo() 时,实际上调用的是 Derived 类中定义的函数,输出结果为 "Derived::foo()"

需要注意的是,在子类中重写虚函数时,其访问权限不能更严格(即不能由 public 变为 private 或 protected),否则编译器会报错

五、使用虚函数后的变化
(1) 对象将增加一个存储地址的空间(32位系统为4字节,64位为8字节)。
(2) 每个类编译器都创建一个虚函数地址表。
(3) 对每个函数调用都需要增加在表中查找地址的操作。

六、虚函数的注意事项
总结前面的内容

(1) 基类方法中声明了方法为虚后,该方法在基类派生类中是虚的。
(2) 若使用指向对象的引用或指针调用虚方法,程序将根据对象类型来调用方法而不是指针的类型。
(3)如果定义的类被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚。

构造函数不能为虚函数。
基类的析构函数应该为虚函数。

  • 首先明确调用顺序:
    • 构造函数:先调用父类的,再调用子类的;不可以为虚函数
    • 折构函数:先调用子类的,再调用父类的;可以是虚函数

友元函数不能为虚,因为友元函数不是类成员,只有类成员才能是虚函数。
如果派生类没有重定义函数,则会使用基类版本。
重新定义继承的方法若和基类的方法不同(协变除外),会将基类方法隐藏;如果基类声明方法被重载,则派生类也需要对重载的方法重新定义,否则调用的还是基类的方法。

虚函数在多态中的应用

虚函数在多态中的应用十分重要。多态指的是同一种类型的对象,在不同的情况下可以表现出不同的行为。C++通过使用虚函数来实现多态性。

虚函数是指在基类中被声明为虚函数并在派生类中重新定义的函数。当基类指针指向派生类对象时,通过该指针调用虚函数时,会根据指向的实际对象类型进行动态绑定,从而执行相应的派生类函数。

例如,有一个基类Animal和两个派生类Cat和Dog,它们都有一个名为sound()的虚函数。当使用基类指针指向Cat或Dog对象时,通过该指针调用sound()函数时会自动执行相应的派生类函数,从而实现了多态性。

虚函数的应用使得程序具有更好的灵活性和可扩展性,并且简化了代码的编写和维护。

虚函数的优缺点

虚函数的优点:

  1. 实现多态性:通过虚函数,可以在不知道对象具体类型的情况下,调用特定对象的方法。
  2. 代码灵活性:虚函数允许子类覆盖父类的方法,并且不需要改变基类的代码。
  3. 代码可维护性:虚函数使得代码易于维护和扩展,因为子类可以通过重载虚函数来实现自己的行为。

虚函数的缺点:

  1. 额外的开销:虚函数需要额外的开销来支持运行时的动态绑定和查找虚表。这可能会影响程序的性能。
  2. 可能会引起混淆:由于虚函数的存在,同名的函数可能会被不同的类定义。如果没有正确的使用虚函数,可能会导致混淆和错误的结果。
  3. 不适合于小型应用:虚函数对于小型应用来说可能过于复杂和冗余。在这种情况下,使用虚函数可能会导致更多的开销而不是提高效率。

如何合理地使用虚函数

  1. 虚函数应用于继承层次结构中的多态性,即通过基类指针或引用调用派生类对象的成员函数。
  2. 可以将虚函数作为接口定义,让不同的派生类实现自己的版本,以满足各自的需求。
  3. 避免在构造函数和析构函数中调用虚函数,因为此时对象还未完全构造或已经被销毁。
  4. 虚函数的声明应该在公共部分(例如基类),而不是在私有部分(例如派生类)中声明。
  5. 将虚函数的实现定义为inline可以提高程序的执行效率。
  6. 在使用纯虚函数时,需要给出其具体实现。可以在派生类中实现,也可以在基类中实现。
  7. 避免过度使用虚函数,因为虚函数会增加程序的开销。在没有必要的情况下,可以使用普通成员函数代替虚函数。


纯虚函数和抽象类

纯虚函数是指在基类中定义的没有实现的虚函数。使用纯虚函数可以使该函数只有函数原型,而没有具体的实现。注:这里的“=0”表示该函数为纯虚函数。

纯虚函数的作用是让子类必须实现该函数,并且不能直接创建该类对象(即该类为抽象类)。

抽象类是包含纯虚函数的类,它们不能被实例化,只能被继承。抽象类只能用作其他类的基类。如果一个类继承了抽象类,则必须实现所有的纯虚函数,否则该类也会成为抽象类。

#include <iostream>
#include <string>
using namespace std;

class Base {
public:
  //纯虚函数
  //只要有一个纯虚函数,这个类称为抽象类
  //抽象类特点:
  // 1、无法实例化对象
  // 2、抽象类的子类 必须要重写父类中的纯虚函数,否则也是属于抽象类

  virtual void func() = 0; //父类的虚函数实现毫无用处,可以直接写成纯虚函数
};

class Son : public Base {
public:
  virtual void func() { cout << "func的调用" << endl; }
};

void test(){
    Base *b = new Son;  //调用通过父类的指针指向子类的对象
    b->func();   //多态的目的让我们的函数接口更通用化,通过一个父类指针,可以调用不同对象下对应的函数

    delete b;
}

int main() {
  test();
  return 0;
}

虚析构纯虚析构

虚析构函数的必要性

C++虚析构函数的必要性在于,当一个类拥有子类并且该类中包含有动态分配的内存资源时,需要通过虚析构函数来释放这些内存资源。如果不使用虚析构函数,当子类实例被删除时,只会调用基类的析构函数,而不会调用子类的析构函数,从而导致子类中动态分配的内存资源无法正确释放,可能会导致内存泄漏或者程序崩溃。因此,在使用继承和动态内存分配时,为了保证程序的正确性和健壮性,需要使用虚析构函数来释放内存资源。(将父类的析构函数写为虚函数或者纯虚函数即可,可以解决父类指针释放子类对象时不干净的问题)

纯虚析构 :需要声明也需要实现,有了纯虚析构之后,这个类也属于抽象类,无法实例化对象。


多重继承中的虚函数

在多重继承中,如果一个类同时继承了多个基类,而这些基类中都有同名的虚函数,那么子类必须对这些虚函数进行重写并实现。此时,需要使用作用域限定符来指明重写的是哪个基类的虚函数。

例如:

class Base1 {
public:
    virtual void func() { cout << "Base1::func()" << endl; }
};

class Base2 {
public:
    virtual void func() { cout << "Base2::func()" << endl; }
};

class Derived : public Base1, public Base2 {
public:
    virtual void func() { Base1::func(); Base2::func(); }
};

例子中,派生类Derived同时继承了Base1和Base2,这两个基类中都有名为func的虚函数。在Derived中,我们通过使用作用域限定符Base1::和Base2::,分别调用了两个基类中的虚函数。

Derive d;
Base1 *b1 = &d; 
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()


小结

摘自:

1、对C++虚函数不了解?看完这篇文章掌握虚函数的原理和作用 - 知乎

2、C++虚函数的作用和实现原理_c++虚函数作用及底层原理-CSDN博客

3、C++虚函数的作用和实现原理_c++ 纯虚函数 父类实现-CSDN博客

4、54 类和对象-多态-多态的原理剖析_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/495517.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LinkWeChat任意文件读取(CVE-2024-0882)

0x01 前言 LinkWeChat 是基于企业微信的开源 SCRM 系统&#xff0c;是企业私域流量管理与营销的综合解决方案。不仅集成了企微强大的后台管理及基础的客户管理功能&#xff0c;而且提供了多种渠道、多个方式连接微信客户。主要运用于电商、零售、教育、金融、政务等服务行业领…

【41-60】计算机网络基础知识(非常详细)从零基础入门到精通,看完这一篇就够了

【41-60】计算机网络基础知识&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了 以下是本文参考的资料 欢迎大家查收原版 本版本仅作个人笔记使用41、使用 Session 的过程是怎样的&#xff1f;42、Session和cookie应该如何去选择&#xff08;适…

【Vue3】实现二维码、链接 分享功能

界面效果: 描述 要实现的功能分别是 1.复制链接可以将次链接分享给他人&#xff0c;他人依靠链接便可以打开你想要让他看到的数据 2.通过微信扫一扫&#xff0c;便可看到和链接一样的内容在手机端 需要的依赖 二维码:qrcode 复制功能:vue-clipboard3 下载二维码:html2canv…

C语言实现顺序表(增,删,改,查)

目录 一.概念&#xff1a; 1.静态顺序表&#xff1a;使用定长数组存储元素。 2.动态顺序表&#xff1a;使用动态开辟的数组存储。 二.顺序表的实现: 1.顺序表增加元素 1.检查顺序表 2.头插 3.尾插 2.顺序表删除元素 1.头删 2.尾删 3.指定位置删 3.顺序表查找元素 …

NSString有哪些创建对象的方法?创建的对象分别存储在什么区域?

NSString有哪些创建对象的方法&#xff1f;创建的对象分别存储在什么区域&#xff1f; 一般通过NSString创建对象的方法有&#xff1a; NSString *string1 "123";NSString *string2 [[NSString alloc] initWithString:"123"];NSString *string3 [NSSt…

Java设计模式—备忘录模式(快照模式)

定义 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;很多软件都提供了撤销&#xff08;Undo&#xff09;操作&#…

白酒:浓香型白酒的典型代表与特点

云仓酒庄的豪迈白酒作为白酒的品牌&#xff0c;具有一系列与众不同的特点和优势。下面云仓酒庄的豪迈白酒将从典型性、品质、口感和包装等方面深入分析白酒的特点&#xff0c;以及它如何体现浓香型白酒的魅力。 浓香型白酒是中国白酒的重要分支&#xff0c;以浓郁的香味和与众不…

设计模式之原型模式讲解

原型模式本身就是一种很简单的模式&#xff0c;在Java当中&#xff0c;由于内置了Cloneable 接口&#xff0c;就使得原型模式在Java中的实现变得非常简单。UML图如下&#xff1a; 我们来举一个生成新员工的例子来帮助大家理解。 import java.util.Date; public class Employee…

SI案例分享--冷却液对PCIe链路性能的影响

目录 0 引言 1 PCIe线缆组件在不同冷却方式中的性能对比 1.1 配置Paddle card互连的电缆组件测试结果&#xff08;cable-1&#xff09; 1.2 直接焊接互连的电缆组件测试结果&#xff08;cable-2&#xff09; 1.3 一侧配置Paddle card、一侧直接焊接互连的电缆组件测试结果…

阿里云账号怎么注册?看这一篇就够了

阿里云账号怎么注册&#xff1f;阿里云账号支持手机号注册、阿里云APP注册、淘宝、支付宝和钉钉多种注册方式&#xff0c;账号注册后需要通过实名认证才可以购买或使用云产品&#xff0c;使用淘宝、支付宝或钉钉注册方式可以免去实名认证步骤&#xff0c;阿里云百科aliyunbaike…

深入解析MD5哈希算法:原理、应用与安全性

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 本文将深入探讨MD5哈希算法的工作原理、应用场景以及安全性问题。我们将了解MD5如何生成固定长度的哈希值&#xff0c;以及它在数…

Leo赠书活动-21期 《一篇讲明白 Hadoop 生态的三大部件》

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

【OpenAI援引马斯克评价中国】小米汽车 SU7 顶配版或超 30 万/OpenAI 加持机器人亮相/荣耀已投入 100 亿研发 AI

雷军&#xff1a;共建一个更良性包容的汽车市场舆论环境 Figure 与 OpenAI 联手推出新机器人 亚马逊和 Google 悄悄降低对生成式 AI 的预期 小米生态链模式大改革&#xff0c;将进行分级管理 掌阅科技&#xff1a;致力打造国内首款真正 AI 阅读应用 荣耀称已投入 100 亿用于 AI…

Ipython与Jupyter之间的关系

IPython 和 Jupyter 之间的关系可以从它们的历史和目标中得到很好的解释。IPython&#xff08;Interactive Python&#xff09;最初是由 Fernando Prez 于 2001 年创建的&#xff0c;旨在提升 Python 的交互式计算体验。它提供了一个强大的交互式 Python shell 和一个面向高效计…

C语言例4-27:计算1+2+...+100之和(利用while语句实现)。

代码如下&#xff1a; //计算12...100之和&#xff08;利用while语句实现&#xff09;。 #include<stdio.h> int main(void) {int n1, sum0;while(n<100){ //复合语句作为当型循环结构的循环体sumsumn;n;}printf("sum %d\n",sum);retu…

Phoenix伪分布安装

引言 Phoenix是构建在HBase上的一个SQL层&#xff0c;能让我们用标准的JDBC APIs而不是HBase客户端APIs来创建表&#xff0c;插入数据和对HBase数据进行查询。Phoenix完全使用Java编写&#xff0c;作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫…

国赛大纲解读

1. 第一部分,是针对5G基础知识的掌握,第二部分是人工智能基本算法的掌握,就是人工智能的应用,用5G+人工智能(AI算法)进行网络优化的问题,要有网络优化的基础知识,比如说:某个区域的覆盖问题,覆盖特别差,但有数据,覆盖电频,srp值这些数据给你,根据数据来判断是…

Rabbitmq消息顺序的问题以及解决方案

1.1消息顺序的场景 场景1&#xff1a;一个queue&#xff0c;多个consumer 一个queue&#xff0c;有多个consumer去消费&#xff0c;这样就会造成顺序的错误&#xff0c;consumer从MQ里面读取数据是有序的&#xff0c;但是每个consumer的执行时间是不固定的&#xff0c;无法保…

1-28 核心类库(四)

一 BigDecimal类(会用) 1.作用:用来进行金融类项目中的数据的精确计算 2. import java.math.BigDecimal; import java.math.RoundingMode;public class BigDecimalTest {public static void main(String[] args) {//一定要字符串 int类型的答案不一定准BigDecimal bd1new B…

专题二_滑动窗口(1)

目录 209. 长度最小的子数组 解析 题解 3. 无重复字符的最长子串 解析 题解 1004. 最大连续1的个数 III 解析 题解 209. 长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 解析 题解 class Solution { public:int minSubArrayLen(int…