【C++】多态

目录

  • 1. 多态的概念
    • 1.1 概念
  • 2. 多态的定义及实现
    • 2.1 多态的构成条件
    • 2.2 虚函数
    • 2.3 虚函数的重写
      • 2.3.1 重写的一些特殊情况
    • 2.4 final和override
    • 2.5 重载、覆盖(重写)、隐藏(重定义)的对比
  • 3. 抽象类
    • 3.1 概念
    • 3.2 实现继承与接口继承
  • 4. 多态的原理
    • 4.1 虚函数表
    • 4.2 多态的原理
      • 4.2.1 为什么不能是子类的指针或引用?为什么不能是父类对象
    • 4.3 动态绑定与静态绑定
  • 5. 单继承和多继承关系中的虚函数表
    • 5.1 单继承中的虚函数表
    • 5.2 多继承中的虚函数表
    • 5.3 菱形继承与菱形虚拟继承
  • 6. 继承和多态性的常见问题

1. 多态的概念

1.1 概念

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

比如买高铁票,成人买票为原价,而学生买则会进行打折等诸如此类的行为都可以使用多态来实现。

2. 多态的定义及实现

2.1 多态的构成条件

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

继承是多态的前提条件

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

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

2.2 虚函数

被virtual关键字修饰的类成员函数即为虚函数:

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

必须是类中的成员函数,普通函数无法被virtual修饰

注意:虚函数与虚继承无关,只是用了同一个关键字

2.3 虚函数的重写

虚函数的重写(覆盖)派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称派生类的虚函数重写了基类的虚函数

注意区别于继承中的重定义(隐藏),重定义是指父子类中只要出现同名的成员就构成重定义(隐藏)。

重写可以归为重定义的子集

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 p;Student s;//传递父类对象Func(p);//切割出子类中父类对象再传递Func(s);return 0;
}

输出结果:

在这里插入图片描述
通过多态调用便实现了传递不同的对象,调用了不同的函数最终产生了不同的结果。

在这里插入图片描述
多态调用必须要严格遵循上面的两个条件,若一个不满足则为普通调用,普通调用只会看当前的对象类型,是什么类型就调用哪个类中的函数

若指定调用父类虚函数时也会使多态调用失效:

void Func(Person& p) {p.Person::BuyTicket();
}

2.3.1 重写的一些特殊情况

  1. 子类重写的虚函数前可以不加virtual来修饰,这是因为子类继承了父类的函数特性,包括虚函数属性,因此,子类中与父类的虚函数完全相同的函数天生就是虚函数,不过可以使用 virtual 关键字来重申这个函数是虚函数,但不是必需的。

为了可读性建议加上

  1. 协变,是指父子类虚函数的返回值可以不同,但必须是父子类关系的指针或者引用(同时),且不可颠倒顺序,并且只要是满足父子关系的类类型都可以。

在这里插入图片描述
3. 析构函数的重写。
析构函数可以声明为虚函数,子类可以对析构函数进行重写,但这貌似不符合重写的条件,因为重写不仅要求为虚函数,还必须要求三同,而父子类的析构函数的名称并不相同,其实不然,在底层编译器会对所有的析构函数的名称进行统一处理为destructor,这样函数名也相同了,因此可以构成重写,如不构成重写在下列场景下会出问题:

在这里插入图片描述
p是一个父类指针,因此既可以指向父类对象也可以指向子类对象,本来期望的是delete子类去调用子类的析构函数,可实际情况是delete子类也是去调用父类的析构,若子类对象中动态开辟的资源时则会造成内存泄漏。
这是因为delete操作符首先会先去调用该类对象的析构函数,p->operator delete()此时的析构函数是普通函数,因此是普通调用,而普通调用看的是调用它的类型,p是Person类型,所以自然会去调用Person类的析构函数,即使p实际指向的是Student类对象。
因此若要解决上述情况,就需要进行多态调用而非普通调用。此时实现多态的两个条件已经满足,p为父类的指针和引用,且可以将析构函数声明为虚函数。

在这里插入图片描述
此时通过多态调用,指向父类去调用父类的析构,指向子类去调用子类的析构即可解决上述情况。

建议父类的析构函数都声明为虚函数

2.4 final和override

C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

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

在这里插入图片描述
若不想让一个类被继承时,也可以在类名后加上final来修饰。

  1. override:检查子类虚函数是否重写了父类某个虚函数,如果没有重写编译报错
    在这里插入图片描述

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

  1. 重载:两个函数在同一个作用域,函数名相同但参数不同(个数、类型、类型顺序)。
  2. 覆盖(重写):两个函数分别在父子类域中,且函数名、参数列表和返回值全都相同(协变除外),两个函数还都得是虚函数。
  3. 覆盖(重写):两个函数(或其它成员)分别在父子类域中,且函数名(成员名)相同。

父子类同名的函数不构成重写就构成重定义。

3. 抽象类

3.1 概念

在虚函数的后面写上 = 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 Func(Car* p) {p->Drive();
}int main() {Benz b;Func(&b);return 0;
}

3.2 实现继承与接口继承

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

4. 多态的原理

4.1 虚函数表

一道笔试题:

// sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};

结果是8字节,这是因为除了存放_b成员以外还要存放一个虚函数表指针:

在这里插入图片描述
vfptr是虚函数表指针,指向的是代码段中虚函数的地址,一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。

虚函数表本质是一个函数指针数组

4.2 多态的原理

当子类继承时没有重写虚函数,那么子类对象虚函数表中保存的虚函数地址是相同的(虚函数表地址不同),若子类重写了虚函数时会发生什么?

在这里插入图片描述
可以发现子类对象的虚表中保存的虚函数地址与父类对象虚表中的地址不同,此时子类的虚表中保存的是重写后的虚函数地址,因此重写也叫覆盖(覆盖了父类的虚函数地址)。

多态调用本质就是通过父类的指针或者引用去对应对象的虚表中查找对应的虚函数地址然后调用它,以此来完成指向父类调父类指向子类调子类的行为。

多态调用是在运行时通过查找对应的虚函数表才能确定函数地址,而普通调用则是在编译时就可以直接确定对应函数的地址。

注意:虚函数的重写,重写的是函数的实现,函数首部的属性使用的还是父类虚函数的首部

子类虚表的生成:a.先将父类中的虚表内容拷贝一份到子类虚表中 b.如果子类重写了父类中某个虚函数,用子类自己的虚函数地址覆盖虚表中父类的虚函数地址 c.子类自己新增加的虚函数按其在子类中的声明次序增加到子类虚表的最后。

4.2.1 为什么不能是子类的指针或引用?为什么不能是父类对象

  1. 因为子类的指针和引用只能指向子类,因此子类无法调用父类中的虚函数始终只能调用子类的函数,这也就无法实现多态
  2. 将子类赋值给父类对象时,并不会拷贝虚表,因此调用时始终是会去调用父类的函数,若拷贝虚表,那么父类对象中的虚表是父类的还是子类的就不确定了,这样便会引起混乱,因此不能是父类对象。

4.3 动态绑定与静态绑定

  1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载。
  2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。

5. 单继承和多继承关系中的虚函数表

5.1 单继承中的虚函数表

typedef void (*fptr)();
void PRINTVFT(fptr* table) {
//vs下最一个位置放的是空指针,其平台不一定for (int i = 0; table[i] != nullptr; ++i) {table[i]();}
}
class Base {
public:virtual void Func1() {cout << "Base::Func1()" << endl;}virtual void Func2() {cout << "Base::Func2()" << endl;}virtual void Func3() {cout << "Base::Func3()" << endl;}
private:int _a = 1;
};class Derive : public Base {
public:virtual void Func1() {cout << "Derive::Func1()" << endl;}virtual void Func4() {cout << "Derive::Func4()" << endl;}
private:int _b = 1;
};int main() {Derive d;PRINTVFT((fptr*)(*(int*)&d));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;
};int main() {Derive d;PRINTVFT((fptr*)(*(int*)&d));cout << endl;//打印出d对象中base2对象中的虚函数// PRINTVFT((fptr*)(*(char*)&d + sizeof(Base1)));Base2* p = &d;PRINTVFT((fptr*)(*(int*)p));return 0;
}

输出:

在这里插入图片描述
多继承的情况与单继承是类似的,只不过虚表的个数会增加,若子类也有自己的虚函数,那么它会依次放到第一个继承下来的父类的虚表的最后的位置。

上面子类重写了两个父类的虚函数,那么两个虚表中存放func1函数的地址应该是一样的,可实际上两个地址并不相同:

在这里插入图片描述
事实上Base1的虚表中保存的才是func1函数真正的地址,将d对象切片给Base1的指针或者引用来调用func1时需要传递this指针,而切出来的Base1对象的地址就是d对象的起始地址,两个地址是重合的,因此可以直接传递this,通过this指针去该对象的虚表中找到对应的函数,若把d对象切片给Base2的指针或引用来调用func1时,由于切出来的Base2对象的地址与d对象的起始地址并不在一个位置,因此传递的this指针是错误的,所以在调用前需要去修正this,让其指向d对象的起始位置然后再去调用func1函数。

int main() {Derive d;d.func1();Base1* p1 = &d;p1->func1();Base2* p2 = &d;p2->func1();return 0;
}

Base1指针调用func1函数:

在这里插入图片描述
eax存放的是this指针,由于p1中保存的地址就是d对象的起始地址,因此可以跳转到func1函数的地址处开始调用:

在这里插入图片描述

Base2指针调用func1:

在这里插入图片描述
与前者步骤一致。

在这里插入图片描述
但这句jmp指令执行完后并没有像前者一样直接进跳转到func1函数的起始位置,而是跳转到了另一个地址处:

在这里插入图片描述
这里让ecx中保存的this指针-8,然后再跳转到func1函数的地址处开始调用:

在这里插入图片描述
-8是因为Base1对象占8个字节,减去后正好指向d对象的起始地址。

减多少取决于前面对象的大小

以上就是修正this指针的过程,本质就是为了不管是哪个父类对象的指针或者引用指向子类,都能在传递this指针时让其指向子类对象的起始地址处然后调用对应重写后的虚函数。

若是普通调用则不会发生上述行为

5.3 菱形继承与菱形虚拟继承

class A {
public:virtual void fun1() { cout << "A::fun1()" << endl; }int _a = 1;
};
class B : public A {
public:int _b = 2;
};
class C : public A {
public:int _c = 3;
};
class D : public B, public C {
public:int _d = 4;
};int main() {D d;return 0;
}

菱形继承只有A类有虚函数时,内存中D类型对象布局如下:

在这里插入图片描述
可以发现与多继承的布局没有区别,但是会有数据冗余和二义性问题。

菱形虚拟继承且只有A类中有虚函数时,内存布局如下:

在这里插入图片描述
此时d对象中只有一份公共的A类对象在下方,同时B和C类对象中增加了虚基表指针。

若B和C类重写了A类的虚函数时会报错,因为此时整个d对象中只有一份公共的虚表 ,如果都重写那虚表中该放哪个呢?因此正确的做法是让最终子类D类也重写虚函数,此时虚表中存放的是D类重写的虚函数,B和C类重写的对于d对象而言就没有意义了,但是若要单独定义B或C类对象时则是有意义的。

更为复杂情况是若B和C有单独的虚函数时,d对象中的B和C类对象会分别增加一张虚表存放各自的虚函数:

在这里插入图片描述

菱形继承时由于B和C类对象中都有一份独立的A类对象,因此各自的虚函数可以直接放到自己继承下来的A类对象的虚表中,互不影响,但此时是虚拟继承,只有一份共享的A类对象,因此各自的虚函数再放到这个共享虚表中就不合理了,因此需要各自单独开辟一个虚表

B和C对象中虚基表和虚函数表的位置:

在这里插入图片描述

需要注意的是,在继承章节那里虚基表的第一个位置存放的是全0,第二个位置是距离公共父类对象的偏移量,而在这里第一个位置存放的是fffffffc即-4,那这个值本质是也是一个偏移量,它是用来找自己虚函数表的位置,在该布局中,虚基表的地址-4就是虚函数表的位置。

若D类也有自己的虚函数时,它是放在B类的虚函数表中的:
在这里插入图片描述

6. 继承和多态性的常见问题

  1. 什么是多态性?

多态性即多种形式,不同的对象在做同样的事或调用同样的函数时,会得到不同的结果。多态分为静态多态和动态多态,静态多态体现在函数重载上,不同的对象类型看似调用同一个函数,实际上编译器根据不同的类型实例化不同的函数,本质上是调用不同的函数。
动态多态体现在继承上,子类覆盖父类的虚函数,当通过父类的指针或引用调用同一函数时,实际上调用的是不同的函数,运行时通过对象中的虚函数表在表中动态查找对应的函数然后调用。
多态性的本质是更方便和灵活地进行各种形式的调用。

  1. 多态的实现原理?

静态多态:通过函数名装饰规则,相同名称的函数被修饰后得到的符号是不同的,不同的符号将被视为不同的函数。

动态多态性:继承+子类覆盖(重写)父类的虚函数,在运行时通过父类的指针或引用在对象的虚表中找到对应的函数,以此来实现指向父类调父类指向子类调子类。

3.内联函数可以是虚函数吗?

可以,但是编译器会忽略内联属性,因为内联函数会在调用它的地方直接展开,并且不会有地址,但是虚函数有地址因为是要进虚函数表,所以内联函数不能是虚函数,即使声明为内联编译器也会直接忽略内联请求。

  1. 静态函数可以是虚函数吗?

不能,静态成员函数可以直接通过类名调用,不需要对象即可调用,但是虚函数不行,虚函数必须通过对象中的虚表指针才能找到,没有对象就不能调用这个虚函数,所以静态成员函数不能成为虚函数。

  1. 构造函数可以是虚函数吗?

不能,构造函数的作用是初始化新创建的对象,虚表指针也是在构造函数的初始化列表中初始化的,即先有构造函数,再有虚表指针。
如果构造函数是虚函数,调用它需要通过虚表指针去找,但是虚表指针需要先在构造函数的初始化列表中进行初始化,这就导致了先有鸡还是先有蛋的哲学问题,所以构造函数不能是虚函数,因为行为不符合构造函数的特征。

  1. 析构函数可以是虚函数吗?

可以,上面提到了一种析构函数必须是虚函数的情况。

  1. 对象访问普通成员函数还是虚函数的速度更快?

首先,如果它是一个普通对象就一样快,因为函数地址可以在编译时直接确定。如果是对象指针或引用,调用普通函数速度快,因为构成多态,得在运行时动态地去对象的虚表中查找对应函数然后调用。

  1. 虚函数表是在什么阶段生成的?存在于哪里?

虚函数表是在编译阶段生成的,存在于代码段中(在常量区中)。

  1. 什么是抽象类?抽象类做什么?

抽象类是指包含纯虚函数的类,不能实例化对象,被子类继承后也不能实例化对象,除非子类重写了纯虚函数,其效果是强制重写虚函数形成多态性,抽象类反映接口继承的关系。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

入门人工智能 ——自然语言处理介绍,并使用 Python 进行文本情感分析(5)

入门人工智能 ——自然语言处理介绍&#xff0c;并使用 Python 进行文本情感分析&#xff08;5&#xff09;&#xff09; 入门人工智能 ——自然语言处理介绍&#xff0c;并使用 Python 进行文本情感分析介绍自然语言处理的挑战NLP的基本任务NLP的基本技术NLP的应用领域 使用 P…

《银河麒麟高级服务器操作系统V10》使用

一言而论&#xff1a;讲了麒麟服务器V10的基本使用&#xff0c;包括终端、VNC 文章目录 前言基本架构环境硬件环境软件环境 麒麟安装步骤1.在宿主机上安装好VM&#xff0c;并且激活2.使用VM创建虚拟机3.启动虚拟机 终端常用点VNC的使用麒麟上安装VNC服务器Windows上安装VNC客户…

自动化测试(五):自动化测试框架的搭建和基于yaml热加载的测试用例的设计

该部分是对自动化测试专栏前四篇的一个补充&#xff0c;本次参考以下文章实现一个完整的谷歌翻译接口自动化测试:   [1]【python小脚本】Yaml配置文件动态加载   [2]【python做接口测试的学习记录day8——pytest自动化测试框架之热加载和断言封装】 目标&#xff1a;框架封…

在visual studio里安装Python并创建python工程

在2009年&#xff0c;云计算开始发力&#xff0c;Python、R、Go这些天然处理批量计算的语言也迅猛发展。微软在2010年&#xff0c;把Python当成一个语言包插件&#xff0c;集成到了visual studio 2010里。在"云优先&#xff0c;移动优先"的战略下&#xff0c;于2015年…

什么是Jmeter?Jmeter使用的原理步骤是什么?

1.1 什么是 JMeter Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于 Web 应用测试&#xff0c;但后来扩展到其他测试领域。 它可以用于测试静态和动态资源&#xff0c;例如静态文件、Java 小服务程序、CGI 脚…

记录一个iOS使用陀螺仪3d效果的抖动问题

使用陀螺仪的时候&#xff0c;遇到一个问题&#xff0c;就是在拖动scrollView滚动的时候&#xff0c;3d效果的图片会抖动 实现3d效果的代码 - (void)updateWithGravityX:(double)gravityXgravityY:(double)gravityYgravityZ:(double)gravityZ {//因为在斜向上45度角的时候&am…

【win10】怎么删除休眠文件

电脑c盘天天爆红&#xff0c;每天可用空间都变少&#xff0c;或者电脑晚上不关机&#xff0c;只锁屏后息屏&#xff0c;第二天发现电脑关机了&#xff0c;可能就是休眠功能惹得鬼。 以下是关闭休眠功能步骤&#xff1a;   1、这个隐藏的系统文件hiberfil.sys&#xff0c;体积…

浅谈C++|STL之算法函数篇

一.遍历常用算法 1.1for_each 在 C 中&#xff0c;for_each 是一个算法函数&#xff0c;位于 <algorithm> 头文件中。它接受一个范围&#xff08;容器或迭代器对&#xff09;以及一个函数对象&#xff08;函数指针、函数、lambda 表达式等&#xff09;&#xff0c;用于…

LiveNVR监控流媒体Onvif/RTSP功能-支持海康摄像头海康NVR通过EHOME协议ISUP协议接入分发视频流或是转GB28181

LiveNVR支持海康NVR摄像头通EHOME接入ISUP接入LiveNVR分发视频流或是转GB28181 1、海康 ISUP 接入配置2、海康设备接入2.1、海康EHOME接入配置示例2.2、海康ISUP接入配置示例 3、通道配置3.1、直播流接入类型 海康ISUP3.2、海康 ISUP 设备ID3.3、启用保存3.4、接入成功 4、相关…

ARMv8架构简介

ARMv8-A架构和处理器 ARMv8-A架构 ARMv8‑A 架构是针对应用程序配置文件的最新一代 ARM 架构。 ARMv8 这个名称用于描述整体架构,现在包括 32 位执行状态和 64 位执行状态。它引入了使用 64 位宽寄存器执行的能力,同时保持与现有 ARMv7 软件的向后兼容性。 ARMv8‑A 架构引…

如何获取美团的热门商品和服务

导语 美团是中国最大的生活服务平台之一&#xff0c;提供了各种各样的商品和服务&#xff0c;如美食、酒店、旅游、电影、娱乐等。如果你想了解美团的热门商品和服务&#xff0c;你可以使用爬虫技术来获取它们。本文将介绍如何使用Python和BeautifulSoup库来编写一个简单的爬虫…

SpringBoot运行原理

目录 SpringBootApplication ComponentScan SpringBootConfiguration EnableAutoConfiguration 结论 SpringbootApplication&#xff08;主入口&#xff09; SpringBootApplication public class SpringbootConfigApplication {public static void main(String[] args) {…

机器学习 day33(误差分析、添加数据)

误差分析 我们可以手动查看分类错误的子集样本&#xff08;通常为100个&#xff09;&#xff0c;并统计他们的错误类型在所有错误类型中&#xff0c;选择一种或几种最常见的错误&#xff0c;进行改进。这可以最高效的改进你的模型误差分析的一个限制是&#xff1a;它只能很好…

CRC(循环冗余校验码的校验方法)

5个关键点&#xff1a; 1.信息码&#xff1a;即给出要校验的二进制码 2.生成多项式&#xff1a;一般多项式会给&#xff0c;从最高位的指数位数就可以得到有几个校验码&#xff1b;如果没给多项式&#xff0c;肯定会给个多项式二进制码&#xff0c;根据它来推就行&#xff08;…

【深度学习】 Python 和 NumPy 系列教程(十六):Matplotlib详解:2、3d绘图类型(2)3D散点图(3D Scatter Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 线框图&#xff08;Wireframe Plot&#xff09; 2. 3D散点图&#xff08;3D Scatter Plot&#xff09; 一、前言 Python是一种高级编程语言&#xff0c;由Guido van Ross…

【Java基础】- RMI原理和使用详解

【Java基础】- RMI原理和使用详解 文章目录 【Java基础】- RMI原理和使用详解一、什么RMI二、RMI原理2.1 工作原理图2.2 工作原理 三、RMI远程调用步骤3.1 RMI远程调用运行流程图3.2 RMI 远程调用步骤 四、JAVA RMI简单实现4.1 如何实现一个RMI程序4.2 JAVA实现RMI程序 一、什么…

微信小程序项目开发Day1

没接触过&#xff0c;直接看视频学习&#xff1a; 千锋教育微信小程序开发制作前端教程&#xff0c;零基础轻松入门玩转微信小程序_哔哩哔哩_bilibili千锋教育微信小程序开发制作前端教程&#xff0c;零基础轻松入门玩转微信小程序共计56条视频&#xff0c;包括&#xff1a;学…

数据分享|R语言逻辑回归、Naive Bayes贝叶斯、决策树、随机森林算法预测心脏病...

全文链接&#xff1a;http://tecdat.cn/?p23061 这个数据集&#xff08;查看文末了解数据免费获取方式&#xff09;可以追溯到1988年&#xff0c;由四个数据库组成。克利夫兰、匈牙利、瑞士和长滩。"目标 "字段是指病人是否有心脏病。它的数值为整数&#xff0c;0无…

MyVector 的实现

myVector #include <iostream> #include <vector>int size20;using namespace std;template <typename type> class myvector {int size;type value;type *arr;public://无参构造myvector(){};//有参构造myvector(int s,type v):size(s),value(v){arrnew in…

jq弹窗拖动改变宽高

预览效果 <div classtishiMask><div class"tishiEm"><div id"coor"></div><div class"topNew ismove"><span class"ismove">提示</span><p onclick"closeTishi()"></p&…