c++进阶------多态

news2024/12/27 13:38:13

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


多态

  • **作者前言**
  • 概念
  • 介绍
  • 虚函数
    • 虚函数重写
      • 虚函数重写的协变
    • 析构函数的重写(基类与派生类析构函数的名字不同)
  • 重载、覆盖(重写)、隐藏(重定义)的对比
  • c++ 11 增加的override 和 final
  • 多态原理
  • 动态绑定和静态绑定
  • 抽象类
    • 接口继承和实现继承
  • 虚函数表存在区域
  • 多继承的多态

概念

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

介绍

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。简单理解就是,不同的对象,调用相同的函数,会产生不一样的结果。

前面我们知道,继承后,不管基类还是派生类调用相同的函数都会产生相同的效果,从这里就可以猜想出,多态和继承的差别了
所以说要想写出多态,有以下条件:

  • 使用virtual修饰函数,(虚函数重写)
    简单的理解就是, 基类和派生类的两个虚函数 要有三同(函数名、参数以及返回值)
    只能修饰函数
  • 必须通过基类的指针或者引用去调用虚函数
    这里我们可以理解一下, 前面我们知道派生类的对象可以赋值给基类的指针、引用以及基类对象, 这里就可以很好的理解为,传入基类对象,调用基类的虚函数,传入派生类的对象,会进行切片,然后调用调用切片后的虚函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
namespace bit
{
	class Person
	{
	public:
		virtual void BuyTicket()
		{
			cout << "我是普通人===》 全票" << endl;
		}
	};
	struct Student : Person
	{
		virtual void BuyTicket()
		{
			cout << "我是学生===》 半票" << endl;
		}
	};


	void BuyT1(Person* People)
	{
		People->BuyTicket();
	}

	void BuyT2(Person& People)
	{
		People.BuyTicket();
	}
}
int main()
{

	bit::Person People1;
	bit::Student stud;
	bit::BuyT1(&People1);
	bit::BuyT1(&stud);
	bit::BuyT2(People1);
	bit::BuyT2(stud);
	return 0;
}

结果:
在这里插入图片描述

虚函数

我们以上面的代码为例子
虚函数:即被virtual修饰的类成员函数称为虚函数

		virtual void BuyTicket()
		{
			cout << "我是学生===》 半票" << endl;
		}

虚函数重写

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

class Person
	{
	public:
		virtual void BuyTicket()
		{
			cout << "我是普通人===》 全票" << endl;
		}
	};
	struct Student : Person
	{
		virtual void BuyTicket()
		{
			cout << "我是学生===》 半票" << endl;
		}
	};

可以看出,派生类对基类进行了虚函数的重写

虚函数重写的协变

虽然虚函数很好理解,但是也会出问题,其中一个问题就叫协变

  1. 协变(基类与派生类虚函数返回值类型不同)
    派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。(了解)
    简单的理解就是我们写出来的虚函数的返回值是基类的地址或者引用,可是我们返回的这个基类的地址或者引用可能是原本基类的,也可能是派生类切片的
    再通俗的说就是,这个返回值必须是基类或者是派生类。否则不构成协变就会报错 ,如果都返回基类不是协变,而是 虚函数重写,
namespace bit
{
	class A
	{
	public:
		 virtual A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
	};
	struct B : A
	{
		virtual B*  funtion()
		{
			cout << "B()" << endl;
			return this;
		}
	};
}
int main()
{
	bit::A a;
	bit::B b;
	a.funtion();
	b.funtion();
	return 0;
}

析构函数的重写(基类与派生类析构函数的名字不同)

普通调用

namespace bit
{
	class A
	{
	public:
		 virtual A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
		 ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		virtual B*  funtion()
		{
			cout << "B()" << endl;
			return this;
		}
		~B()
		{
			cout << "~B()" << endl;
		}
	};
		int main()
{
	bit::A *a = new bit::A;
	bit::A *b = new bit::B;
	delete a;
	delete b;
	return 0;
}

结果:

在这里插入图片描述
可以看到,这是是普通的调用。这里是有内存泄漏的情况,进而可以得出: 普通调用,看的是指针、引用或者对象的类型,所以说图中的a和b释放的话只能调用基类的析构,调用不了派生类的析构,要想各自调用各自的,可以进行虚函数重写

前面我们知道,多态的调用主要是看基类的指针或者引用,基类的话调用基类的,派生类切片的就调用派生类的,在这里的话,多态还要满足一个条件,就是两个虚函数要三同,可是析构函数的函数名是不同的,还是可以进行虚函数 ,
原因:
编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

namespace bit
{
	class A
	{
	public:
		 virtual A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
		 virtual ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		virtual B*  funtion()
		{
			cout << "B()" << endl;
			return this;
		}
		virtual ~B()
		{
			cout << "~B()" << endl;
		}
	};
}


结果:
在这里插入图片描述

还有一些情况:
基类去掉virtual,派生类不去掉,不构成虚函数重写,但是构成隐藏

class A
	{
	public:
		  A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
		 virtual ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		virtual B*  funtion()
		{
			cout << "B()" << endl;
			return this;
		}
		virtual ~B()
		{
			cout << "~B()" << endl;
		}
	};
int main()
{
	bit::A *a = new bit::A;
	bit::A *b = new bit::B;
	a->funtion();
	b->funtion();
	(new bit::B)->funtion();
	delete a;
	delete b;

	return 0;
}

在这里插入图片描述

这种情况的话基类的funtion()和派生类的funtion()构成隐藏关系,不是虚函数重写

派生类去掉virtual,基类不去掉,构成虚函数重写

	class A
	{
	public:
		  virtual A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
		 virtual ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		 B*  funtion()
		{
			cout << "B()" << endl;
			return this;
		}
		virtual ~B()
		{
			cout << "~B()" << endl;
		}
	};
	int main()
{
	bit::A *a = new bit::A;
	bit::A *b = new bit::B;
	a->funtion();
	b->funtion();
	(new bit::B)->funtion();
	delete a;
	delete b;

	return 0;
}

在这里插入图片描述

一般建议这样写,因为怕别人看不懂,
如果这些难记的话,就简单的记得,只要基类使用了virtual修饰函数,派生类写一样的函数就是虚函数(无论是否加了virtual修饰)

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

在这里插入图片描述
还有一些需要注意, 派生类调用父类的成员,使用的是父类对象进行调用(过程有切片),而不是派生类对象,
还要多态调用的重写,是派生类对基类虚函数的实现重写(基类虚函数的函数体拷贝一份到派生类的虚函数中,内容自己重写来覆盖掉,也就是为啥可以不加virtual也可以构成虚函数)

c++ 11 增加的override 和 final

实现一个类不能被继承的话有几种方法

  • 父类构造函数私有化,派生类实例不了对象

  • final修饰的类为最终类,不能被继承
    final:修饰虚函数,表示该虚函数不能再被重写

class A
	{
	public:
		virtual  A* funtion() final
		{
			cout << "A()" << endl;
			return this;
		}
		 virtual ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		virtual B*  funtion()//会报错
		{
			cout << "B()" << endl;
			return this;
		}
		virtual ~B()
		{
			cout << "~B()" << endl;
		}
	};

检查虚函数是否被派生类重写的话, 可以增加override进行修饰
override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class A
	{
	public:
		virtual  A* funtion()
		{
			cout << "A()" << endl;
			return this;
		}
		 virtual ~A()

		 {
			 cout << "~A()" << endl;

		}
	};
	struct B : A
	{
	public:
		virtual B*  funtion() override
		{
			cout << "B()" << endl;
			return this;
		}
		virtual ~B() override
		{
			cout << "~B()" << endl;
		}
	};

多态原理

在讲这个我们先做一道题

class A
	{
	public:

		void funtion()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion1()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion2()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion3()
		{
			cout << "我爱中国" << endl;
		}
	protected:
		int _b;

	};
int main()
{
	A a;

	cout << sizeof(a) << endl;

	return 0;
}

我们看看sizeof(A)的大小是(64位下的)
在这里插入图片描述
这个结果看过去不太符合内存对齐,有这个效果是因为,拥有虚函数的类会增加一个成员_vfptr
在这里插入图片描述
在这里插入图片描述

这个_vfptr是一个指针,这个指针我们叫做虚函数表指针(v代表virtual,f代表function),一个含有虚函数的类中都至少都有一个虚函数表指针,虚函数指针要存放在虚函数表里面,虚函数表也简称虚表 ,所以说_vfprt也叫虚表指针,

然后我们看看派生类的虚表
写一个派生类

struct B : A
{
	virtual void funtion1()
	{
		cout << "我非常的爱中国" << endl;
	}
protected:
	int _bb;
};
int main()
{
	A a;
	B b;
	cout << sizeof(a) << endl;

	return 0;
}

可以看到b对象的里面的成员
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果细心的发现可以看出基类的虚表和派生类的虚表不一样,我们大致可以想到,派生类的虚表是拷贝基类的过来,然后如果发现派生类对基类的虚函数进行了重写,就会把重写后的派生类虚函数指针覆盖基类的虚函数指针,进而达到了多态效果

多态调用的原理: 运行时,去虚函数表里面找虚函数指针,进行调用,指针指向基类虚函数就调用基类的虚函数,否则反之(运行时找到地址))

普通调用的原理: 编译时,通过类型去确定函数地址(编译时找到地址))
总结:
虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr(不同的编译器有不同的做法-)。对象里面存放的是虚表指针,不是虚表
在这里插入图片描述

a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。需要注意的是在VS中的监视窗口是不会显示出来的,需要看内存区才能看到派生类的虚函数地址

虚函数以及普通函数编译完后,就是一段指令, 所以说虚函数和普通函数都存放在代码段中

动态绑定和静态绑定

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

抽象类

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

//抽象类
class A
{
public: 
	virtual void funtion() = 0;//纯虚函数
};

struct B : A
{
public:
	virtual void funtion()
	{
		cout << "我喜欢中国" << endl;
	}
};
struct c : A
{
}
int main()
{
	bit::A a;//不能创建
	bit::B b;//可以创建
	bit::C c;//不能创建
	b.funtion();
	return 0;
}

接口继承和实现继承

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

虚函数表存在区域

前面我们知道,指针类型决定解引用访问多大的字节, 比如 int* 解引用访问四个字节,char* 访问一个,地址类型进行强转(类型相近才可以强转,指针类型可以随意), 所以可以通过这个方法来判断内存的大小端存储,如果我们要获取到虚函数的地址, 我们可以根据这个方法来, 先获取到对象的地址,然后强转成int* ,然后解引用,就可以获取到四个字节的内容,也就是虚函数的地址,
我们还是利用上面的代码来

namespace bit
{
	class A
	{
	public:

		void funtion()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion1()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion2()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion3()
		{
			cout << "我爱中国" << endl;
		}
	protected:
		int _b;

	};
	struct B : A
	{
	public:
		virtual void funtion1()
		{
			cout << "我非常爱中国" << endl;
		}
	protected:
		int _bb;
	};
}
int main()
{
	bit::A a;
	bit::A* p = &a;
	int* pa = (int*)p;//获取到虚函数表的地址
	const char* ch = "ssssss";// 存放在常量区
	static int digth = 0;//存放在静态区
	printf("bit::A      :%p\n", *pa);
	printf("const char* :%p\n", ch);
	printf("static int* :%p\n", &digth);
	
	return 0;
}

在这里插入图片描述
可以看出前两个的地址相近不远,所以可以判断虚函数表存放在常量区中

我们也可以根据地址来调用函数

int main()
{
	bit::A a;
	bit::A* p = &a;
	int* pa = (int*)p;
	for (int i = 0; (void(*)())(((int*)(*pa))[i]) != nullptr; i++)
	{
		((void(*)())(((int*)(*pa))[i]))();
		
	}
	return 0;
}

在这里插入图片描述

c/c++的内存分配

  1. 栈(stack):又称堆栈,栈是由编译器自动分配释放,存放函数的参数值,局部变量的值等(但不包括static声明的变量,static意味着在数据段中存放变量)。除此之外,在函数被调用时,栈用来传递参数和返回值。由于栈的先进后出的特点,所以栈特别方便用来保存/恢复调用数据。其操作方式类似于数据结构中的栈。
  2. 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小,并不固定,可动态扩张或缩放。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被提出(堆被缩减)。堆一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
  3. 全局数据区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域。
  4. 文字常量区:常量字符串就是放在这里,程序结束后由系统释放。
  5. 程序代码区:存放函数体的二进制代码

需要注意的是, 多个相同的对象,使用的虚函数表是一样的,

总结:
虚函数表位于只读数据段(.rodata),即:C++内存模型中的常量区;
虚函数代码则位于代码段(.text),也就是C++内存模型中的代码区

多继承的多态

前面我们写的都是单继承,现在我们来讲一讲多继承的多态

namespace bit
{
	class A
	{
	public:

		void funtion()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion1()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion2()
		{
			cout << "我爱中国" << endl;
		}
		virtual void funtion3()
		{
			cout << "我爱中国" << endl;
		}
	protected:
		int _a;

	};
	struct B 
	{
	public:
		void funtion4()
		{
			cout << "我非常爱中国" << endl;
		}
		virtual void funtion5()
		{
			cout << "我非常爱中国" << endl;
		}
		virtual void funtion6()
		{
			cout << "我非常爱中国" << endl;
		}
	protected:
		int _bb;
	};
	struct C : A, B // 先继承的先初始化
	{
	public:

		virtual void funtion1()
		{
			cout << "我超喜欢中国" << endl;
		}
		virtual void funtion5()
		{
			cout << "我超喜欢中国" << endl;
		}
		virtual void funtion9()
		{
			cout << "我超喜欢中国" << endl;
		}
	protected:
		int _c;
	
	};
}
int main()
{
	bit::A a;
	bit::B b;
	bit::C c;
	cout << sizeof(c);


	return 0;
}

结果:
在这里插入图片描述
可以看到c里面有两个虚表指针,派生类和基类的虚表不一样,这点不难,
我们需要思考的是,funtion9函数的指针放在哪里呢,
代码如下:

int main()
{
	bit::C c;
	bit::A* a = &c;
	bit::B* b = &c;
	for (int i = 0; (int*)(((int*)(*(int*)a))[i]) != nullptr; i++)
	{
		printf("[%d]:%p->",i, (((int*)(*(int*)a))[i]));
		((void(*)())(((int*)(*(int*)a))[i]))();
	}
	cout << endl;
	for (int i = 0; (int*)(((int*)(*(int*)b))[i]) != nullptr; i++)
	{
		printf("[%d]:%p", i, (((int*)(*(int*)b))[i]));
		((void(*)())(((int*)(*(int*)b))[i]))();
	}
	
	cout << sizeof(c);


	return 0;
}

结果:
在这里插入图片描述
可以看出,派生类没有的虚函数的指针存放在第一张虚函数表里面,而不是第二张
依次也可以说明,一个类可以有多个虚表。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习/数据分析--通俗语言带你入门线性回归(结合案例)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 机器学习是深度学习和数据分析的基础&#xff0c;接下来将更新常见的机器学习算法注意&#xff1a;在打数学建模比赛中&#xff0c;机器学习用的也很多&a…

探索GitLab:从搭建到高效使用的实用指南

企业里为什么喜欢使用GitLab 一、GitLab简介二、搭建GitLab三、GitLab的权限管理3.1、用户注册3.2、创建用户组3.3、为用户组添加用户3.4、为工程添加访问权限 四、GitLab的code review五、团队知识管理六、总结 一、GitLab简介 GitLab是利用 Ruby on Rails 一个开源的版本管理…

Adobe Media Encoder ME 2023-23.6.6.2 解锁版下载安装教程 (专业的视频和音频编码渲染工具)

前言 Adobe Media Encoder&#xff08;简称Me&#xff09;是一款专业的音视频格式转码软件&#xff0c;文件格式转换软件。主要用来对音频和视频文件进行编码转换&#xff0c;支持格式非常多&#xff0c;使用系统预设设置&#xff0c;能更好的导出与相关设备兼容的文件。 一、…

网站怎么做敏感词过滤,敏感词过滤的思路和实践

敏感词过滤是一种在网站、应用程序或平台中实现内容审查的技术&#xff0c;用于阻止用户发布包含不适当、非法或不符合政策的内容。我们在实际的网站运营过程中&#xff0c;往往需要担心某些用户发布的内容中包含敏感词汇&#xff0c;这些词汇往往会导致我们的网站被用户举报&a…

JVM的组成

JVM 运行在操作系统之上 java二进制字节码文件的运行环境 JVM的组成部分 java代码在编写完成后编译成字节码文件通过类加载器 来到运行数据区,主要作用是加载字节码到内存 包含 方法区/元空间 堆 程序计数器,虚拟机栈,本地方法栈等等 随后来到执行引擎,主要作用是翻译字…

系统工程与信息系统(上)

系统工程 概念 【系统工程】是一种组织管理技术。 【系统工程】是为了最好的实现系统的目的&#xff0c;对系统的组成要素、组织结构、信息流、控制机构进行分析研究的科学方法。 【系统工程】从整体出发、从系统观念出发&#xff0c;以求【整体最优】 【系统工程】利用计算机…

信息搜集--敏感文件Banner

免责声明:本文仅做分享参考... git安装: Windows10下安装Git_win10安装git好慢-CSDN博客 git目录结构: Git 仓库目录 .git 详解-CSDN博客 敏感目录泄露 1-git泄露 Git是一个开源的分布式版本控制系统,我们简单的理解为Git 是一个*内容寻址文件系统*&#xff0c;也就是说Gi…

二十四、解释器模式

文章目录 1 基本介绍2 案例2.1 Instruction 接口2.2 StartInstruction 类2.3 PrimitiveInstruction 类2.4 RepeatInstruction 类2.5 InstructionList 类2.6 Context 类2.7 Client 类2.8 Client 类的运行结果2.9 总结 3 各角色之间的关系3.1 角色3.1.1 AbstractExpression ( 抽象…

Nexpose漏扫

免责声明:本文仅做分享参考... nexpose官网: Nexpose On-Premise Vulnerability Scanner - Rapid7 Rapid7的Nexpose是一款非常专业的漏洞扫描软件。有community版本和enterprise版本。 其中community版是免费的&#xff0c;但是功能简单&#xff1b;enterprise版本功能强大.…

适用于 Windows 10 的最佳免费数据恢复软件是什么?

有没有适用于 Windows 10 的真正免费的数据恢复软件&#xff1f; 丢失重要数据&#xff0c;无论是由于硬件问题、软件问题、意外删除、格式化还是病毒和恶意软件&#xff0c;确实很麻烦。当你面临数据丢失时&#xff0c;你可能真心希望找到一款免费的数据恢复软件&#xff0c;…

【C++指南】深入剖析:C++中的引用

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 引言&#xff1a; 一、引用的基本概念 1. 定义与特性 2. 语法与声明 二、引用的进阶用法 1. 函…

Python高手参考手册:迭代器协议详解

在Python中&#xff0c;迭代器协议提供了一种灵活的方式来遍历容器中的元素。本文将详细介绍迭代器协议的基础知识、其实现机制以及一些实用的应用案例。 迭代器协议概述 1.1 迭代器与迭代协议 迭代器协议定义了如何遍历容器中的元素。在Python中&#xff0c;任何实现了迭代…

Android:使用Gson常见问题(包含解决将Long型转化为科学计数法的问题)

一、解决将Long型转化为科学计数法的问题 1.1 场景 将一个对象转为Map类型时&#xff0c;调用Gson.fromJson发现&#xff0c;原来对象中的long类型的personId字段&#xff0c;被解析成了科学计数法&#xff0c;导致请求接口失败&#xff0c;报参数错误。 解决结果图 1.2、Exa…

轻松掌握域名系统(DNS):基础小白的入门指南

文章目录 域名系统概述DNS 的发展和结构DNS 的服务和功能互联网的域名结构域名服务器的类型和功能域名解析的过程DNS资源记录小结 域名系统概述 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09; 是一种核心服务&#xff0c;它使得网络应用能够在应用层使用…

腾讯地图SDK Android版开发 6 显示覆盖物

腾讯地图SDK Android版开发 6 显示覆盖物 前言地图类中覆盖物的接口覆盖物类Marker示例Polyline示例Polygon示例Arc示例Circle示例移除示例效果图 Marker的更多属性常用属性交互碰撞动画其它属性 折线的更多属性常用属性交互其它属性 多边形的更多属性常用属性交互其它属性 Arc…

【手抖拜拜!特发性震颤患者的专属锻炼秘籍,轻松改善生活品质】

Hey小伙伴们~&#x1f44b; 今天咱们来聊聊一个可能不那么常被提及&#xff0c;但却实实在在影响着很多人生活质量的话题——特发性震颤。如果你或你身边的人正为此困扰&#xff0c;别怕&#xff0c;这篇笔记就是你们的“稳手宝典”&#x1f4da;&#xff01; &#x1f308; 了…

【论文复现】 | 改善抑郁估计从面部对齐,训练优化和调度

文章目录 1、Introduction2、Related work3、 Proposed methodology3.1. Preprocessing and face alignment3.2 Architecture3.3 Data Augmentation3.4 Training 4、Experimental analysis and results4.1 Datasets4.2 Experimental setup4.3 Protocol and Performance metrice…

JS面试题3

1、使用递归完成1~100的累加 // 1~100累加function addRes(x,y){if(x y){return x}else{return y addRes(x,y-1) // 第一次&#xff1a;3 addRes(1,2) > 往后依次递减&#xff0c;到达目标值位置后依次计算累加值}}console.log(addRes(1,3))// 斐波那契数列(兔子数列) &…

【HTML5+JavaScript+CSS3】3D空间环绕旋转特效(效果+源码+可理解的注释)

失去一个人,只是生命中的一段过程,天空不会永远都下雨,我们总会在最深的绝望里,看见最美的风景。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人…

案例研究|JumpServer堡垒机为金山办公信息安全保驾护航

金山办公&#xff08;KINGSOFT OFFICE&#xff09;是中国办公软件的领航者&#xff0c;其产品体系以旗舰产品WPS Office为核心&#xff0c;辅以金山文档、金山协作及金山词霸等产品&#xff0c;形成了全方位、高效能的办公生态系统&#xff0c;在中国乃至全球范围内拥有庞大的用…