【C++学习】类和对象--多态

news2024/9/25 13:25:21

多态的基本语法

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

静态多态:

        函数重载和运算符重载属于静态多态,复用函数名(函数地址早绑定,编译阶段确定函数地址)

动态多态:

        派生类和虚函数实现运行时多态(函数地址晚绑定,运行阶段确定函数地址)

 案例:

#include<iostream>
using namespace std;
class animal
{
public:
	void speak()
	{
		cout << "animal is speaking" << endl;
	}
	/*改成下面就可以变成小猫说话了,即多态
	virtual void spaek()//虚函数
	{
		cout << "animal is speaking" << endl;
	}
	*/
};
class cat :public animal
{
public:
	void speak()//virtual可写可不写
	{
		cout << "cat is speaking" << endl;
	}
};
void dospeak(animal& animal)//animal &animal=cat;
{//C++中允许父子间的类型转换(无需强制转换)
 //父类的指针或引用可以直接指向子类对象

	animal.speak();

//不管传入什么都会走animal里的speak函数
//因为它是地址早绑定   在编译阶段就确定了函数地址
//如果想让传进来的动物说话  就要让地址晚绑定
}
int main()
{
	cat cat;
	dospeak(cat);
	system("pause");
	return 0;
}

输出:

 但是这里dospeak传入的是cat,我们是想要cat说话

动态多态满足条件

1.有继承关系

2.子类重写父类中的虚函数

(重写:函数返回类型,函数名,参数列表完全相同)

(重载:参数类型不同)

使用:父类的指针或引用  指向子类对象

原理剖析

不管是几级、什么类型,指针都占4个字节

当子类重写父类的虚函数。

子类中的虚函数  内部  会替换成  子类的虚函数地址。

当父类的指针或者引用指向子类对象的时候,发生多态。

animal& animal=cat;//等号左边编译类型,右边运行类型
//早绑定看编译类型,晚绑定看运行类型

animal.speak();//
## 来自你的消息:
C++多态的原理是什么

## 来自ChatAI的消息:
C++中的多态是指通过基类的指针或引用调用派生类的虚函数,
实现不同对象之间的不同行为。其原理是基于C++中的虚函数和动态绑定机制。

在C++中,通过在基类中声明一个虚函数,并在派生类中重写该虚函数,
可以实现基类指针或引用调用派生类对象时,动态绑定到正确的函数实现。
在运行时,程序会根据对象的类型动态选择调用的函数实现,这一过程称为动态绑定。
这样就可以实现不同的对象之间的不同行为。

C++中的虚函数通过虚函数表(Virtual Table)来实现,每个类中含有一个虚函数表,
表中记录了指向各个虚函数地址的指针。在派生类中重写虚函数时,
会替换掉该函数在虚函数表中原有的指针,从而实现动态绑定。

总的来说,C++多态的原理是基于虚函数和动态绑定机制实现的,
通过基类指针或引用调用派生类对象时,根据对象的类型动态选择调用的函数实现,
实现不同对象之间的不同行为。

## 来自你的消息:
函数地址早绑定和晚绑定是怎么实现的

## 来自ChatAI的消息:
函数地址早绑定和晚绑定是面向对象编程中的两个概念,
早绑定是指在编译时将函数地址绑定到调用点,晚绑定则是在运行时才将函数地址绑定到调用点。

1. 早绑定

在早绑定中,函数地址在编译时就已经确定,
编译器会在函数调用点直接将函数地址嵌入到生成的汇编代码中。
这样可以提高程序的执行效率,因为在运行时不需要动态绑定函数地址,
直接跳转到函数入口就可以执行函数。

例如,C++中非虚函数的调用,就是早绑定的方式。
在调用非虚函数时,编译器已经知道调用的具体函数实现,
因此生成的汇编代码中直接嵌入了函数地址。

2. 晚绑定

在晚绑定中,函数地址要在运行时动态绑定到调用点。
晚绑定主要用于实现面向对象编程中的多态特性。
在多态中,同一个函数可能有多个不同的实现,根据具体的对象类型动态决定调用哪一个实现。

例如,在C++中使用虚函数时,虚函数地址是在运行时根据对象的类型动态绑定的,
这样可以实现动态多态调用。

晚绑定的实现原理是使用虚函数表,每个类的对象都有一个指向自身虚函数表的指针,
虚函数表中存储着类中虚函数的地址,派生类会重写基类的虚函数,
并且在自己的虚函数表中设置指向新函数的地址,这样就可以实现根据对象类型动态绑定函数地址。

总的来说,函数地址的早绑定和晚绑定实现的方式不同,早绑定在编译时就决定函数地址,
而晚绑定需要在运行时根据具体对象类型动态绑定函数地址,主要用于实现多态特性。

案例1-计算器类

多态优点:

              代码组织结构清晰

              可读性强

              利于前期、后期的扩展和维护

对多态的理解:同样是购票,不同的对象有不同的结果。

普通写法

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

class calculator
{
public:
	int getresult(string op)
	{
		if (op == "+") return m_num1 + m_num2;
		if (op == "-") return m_num1 - m_num2;
		if (op == "*") return m_num1 * m_num2;
	}
	int m_num1;
	int m_num2;
};
void test01()
{
	calculator c;
	c.m_num1 = 10;
	c.m_num2 = 10;
	cout << c.getresult("+") << endl << c.getresult("-") << endl << c.getresult("*") << endl;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

弊端:如果要新增操作  除法  ,就需要修改   getresult  里面的代码。

在真实开发中提倡开闭原则:对扩展进行开放,对修改进行关闭

多态写法

#include<iostream>
#include<string>
using namespace std;
//利用多态实现计算器

class calculator//先实现计算器抽象类
{
public:
	virtual int getresult()
	{
		return 0;
	}
	int m_num1;
	int m_num2;
};
class addcalculator :public calculator
{
public:
	virtual int getresult()
	{
		return m_num1 + m_num2;
	}
};

class subcalculator :public calculator
{
public:
	virtual int getresult()
	{
		return m_num1 - m_num2;
	}
};

class mulcalculator :public calculator
{
public:
	virtual int getresult()
	{
		return m_num1 * m_num2;
	}
};
void test02()
{//多态使用条件
//父类指针或引用指向子类对象

	//加法运算
	calculator* abc = new addcalculator;
	abc->m_num1 = 10;
	abc->m_num2 = 10;
	cout << abc->getresult() << endl;
	
	delete abc;//用完后记得销毁

	//减法运算
	abc = new subcalculator;
	//指针本身是父类指针,释放的是堆区的数据,但是指针本身的类型并没有改变
	abc->m_num1 = 100;
	abc->m_num2 = 120;
	cout << abc->getresult() << endl;
	delete abc;

	//乘法运算
	abc = new mulcalculator;
	//指针本身是父类指针,释放的是堆区的数据,但是指针本身的类型并没有改变
	abc->m_num1 = 10;
	abc->m_num2 = 12;
	cout << abc->getresult() << endl;
}
int main()
{
	test02();
	system("pause");
	return 0;
}

便于修改,可读性强。前后期便于扩展和维护。

C++开发提倡利用多态设计程序架构,因为多态优点很多

纯虚函数和抽象类

在多态中,通常父类中的虚函数的实现是毫无意义的,主要都是调用子类重写的内容。

因此,可以将虚函数改为纯虚函数

纯虚函数语法:virtual  返回值类型   函数名(参数列表)=0;

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

        无法实例化对象

        子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

#include<iostream>
using namespace std;
class Base
{
public:
	virtual void func() = 0;//在虚函数的前提下=0,被认为是全虚函数
	//只要有一个纯虚函数,这个类就称为抽象类
	//1.无法实例化对象
	//2.抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类
};
class Son :public Base
{
public:
	virtual void func()
	{
		cout << "Son" << endl;
	}
};
void test01()
{
	/*Base b;
	new Base;
	Son s;
	无论是堆区还是栈区,抽象类都无法实例化对象
	*/
	//Son s;//子类必须要重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

案例2-制作饮品

#include<iostream>
using namespace std;
class drinking
{
public:
	//煮水
	virtual void boil() = 0;
	//冲泡
	virtual void brew() = 0;
	//倒入杯中
	virtual void pour() = 0;
	//加入辅料
	virtual void add() = 0;
	//制作
	void make()
	{
		boil();
		brew();
		pour();
		add();
	}
};
class coffee:public drinking
{
public:
	//煮水
	virtual void boil()
	{
		cout << "煮农夫山泉" << endl;
	}
	//冲泡
	virtual void brew()
	{
		cout << "冲泡咖啡" << endl;
	}
	//倒入杯中
	virtual void pour()
	{
		cout << "倒入杯中" << endl;
	}
	//加入辅料
	virtual void add()
	{
		cout << "加入糖和牛奶" << endl;
	}
};
class tea :public drinking
{
public:
	//煮水
	virtual void boil()
	{
		cout << "煮山泉水" << endl;
	}
	//冲泡
	virtual void brew()
	{
		cout << "冲泡茶叶" << endl;
	}
	//倒入杯中
	virtual void pour()
	{
		cout << "倒入杯中" << endl;
	}
	//加入辅料
	virtual void add()
	{
		cout << "加入枸杞" << endl;
	}
};
void dowork(drinking *abs)
{
	abs->make();
	delete abs;//做完就释放
}
void test01()
{
	//做咖啡
	dowork(new coffee);
	cout << "---------------------------------------------------" << endl;
	//做茶
	dowork(new tea);
}
int main()
{
	test01();
	system("pause");
	return 0;
}

虚析构和纯虚析构

多态使用时,如果子类中有属性开辟在堆区,那么子类指针在释放时无法调用到子类的析构代码。

解决方法:将父类中的析构函数改为虚析构或纯虚析构。

虚析构与纯虚析构共性:

        可以解决父类指针释放子类的对象

        都需要有具体的函数实现

虚析构和纯虚析构区别:

        如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构:            virtual     类名(){}

纯虚析构:         virtual  ~类名(){}

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3.拥有纯虚析构函数的类也属于抽象类

## 来自你的消息:
C++中虚析构和纯虚析构分别是什么,你能详细解释一下吗

## 来自ChatAI的消息:
C++中的虚析构和纯虚析构都用于处理多态的情况下的内存回收问题。

虚析构函数是一个在基类中被声明为虚函数的析构函数。
这意味着,在一个子类对象被删除时,先执行子类的析构函数,再执行父类的析构函数。
这是因为如果只执行子类的析构函数,它的成员变量和指针可能指向父类的对象或其他资源,
这样就可能导致内存泄漏或不可预期的错误。

纯虚析构函数是一个在基类中被声明为纯虚函数的析构函数。
它没有任何实现,只是声明。纯虚析构函数的作用是使一个类成为抽象类,即不能被实例化。
由于一个类的析构函数一定会被调用,所以如果一个类想要成为抽象类,
就必须在其中声明一个纯虚析构函数。

需要注意的是,一个类中只要含有纯虚函数,就不能被实例化,
即不能创建该类的对象,因此纯虚析构函数的存在也起到了一个阻止实例化的作用。
而虚析构函数可以有实现也可以没有,它的主要作用是在多态的情况下确保正确的内存回收。

## 来自你的消息:
为什么基类里的析构函数不能保证内存的释放,加上virtual关键字智慧就可以了?

## 来自ChatAI的消息:
基类中的析构函数没有使用 `virtual` 关键字时,在多态情况下,
如果一个子类对象被删除时,只会调用子类的析构函数而不会调用基类的析构函数,
因为在编译时只知道对象的静态类型,不知道其动态类型,
因此也不知道需要调用哪一个析构函数,这样就无法实现多态时的正确内存回收,
容易导致内存泄漏等问题。

当在基类中加上 `virtual` 关键字使其变为虚析构函数时,
当通过基类的指针或引用删除一个子类对象时,会根据该对象的动态类型来调用正确的析构函数,
实现多态时的正确内存回收。因为使用 `virtual` 关键字会在对象的 Vtable 中为其生成一个虚函数表,包含了类中所有的虚函数和对应的偏移量,使得程序在运行时可以通过动态绑定来确定正确的析构函数。

需要注意的是,只有当类中至少有一个虚函数时,该类才会生成虚函数表,
从而使得类能够使用虚功能,包括虚析构函数。

## 来自你的消息:
虚析构和纯虚析构有什么区别?

## 来自ChatAI的消息:
虚析构函数和纯虚析构函数都可以用于按照规范释放动态分配的内存。
它们主要的区别在于:

1. 纯虚析构函数是一个纯虚函数,没有函数体,即无需提供实现,
但是必须在子类中被重新实现,否则子类也变成了抽象类。
而虚析构函数是一个有实现体的虚函数,通常需要在基类中提供一个默认的实现,
在子类中根据需要进行重写。

2. 如果一个类定义了纯虚析构函数,在该类中不需要为其进行实现,
但在派生类中必须要实现,否则会导致编译错误。而对于虚析构函数,
可以在基类中提供一个默认的实现,子类可以选择继承该实现或者根据需要重新实现。
如果不需要重新实现,则可以在子类中不写任何代码。

3. 纯虚析构函数的主要作用是定义一个基类,使得该基类不能够被实例化。
而虚析构函数的主要作用是定义一个可被实例化的基类,并用于在析构时正确地回收多态对象的内存。

因此,虚析构和纯虚析构主要的应用场景不同。虚析构主要用于处理多态情况下的内存释放问题,
而纯虚析构通常用于定义一个抽象基类,以禁止该类被直接实例化。
#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
	animal()
	{
		cout << "animal的构造函数调用" << endl;
	}
	~animal()
	{
		cout << "animal的析构函数调用" << endl;
	}
	virtual void speak() = 0;
};
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat的构造函数调用" << endl;
		m_name = new string(name);
	}
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
	~cat()
	{
		cout << "cat析构函数调用" << endl;
		if (m_name != NULL) delete m_name, m_name = NULL;
	}
	string* m_name;
};
void test01()
{
	animal* animal = new cat("tom");
	animal->speak();
	//父类指针在析构的时候,不会调用子类中析构函数
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

注意这里出现了问题,少了    cat的析构函数调用。

 解决:利用虚析构解决父类指针释放子类对象不干净的问题。

#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
	animal()
	{
		cout << "animal的构造函数调用" << endl;
	}
	virtual ~animal()
	{
		cout << "animal的析构函数调用" << endl;
	}
	virtual void speak() = 0;
};
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat的构造函数调用" << endl;
		m_name = new string(name);
	}
	virtual void speak()
	{
		cout << "小猫在说话" << endl;
	}
	~cat()
	{
		cout << "cat析构函数调用" << endl;
		if (m_name != NULL) delete m_name, m_name = NULL;
	}
	string* m_name;
};
void test01()
{
	animal* animal = new cat("tom");
	animal->speak();
	//父类指针在析构的时候,不会调用子类中析构函数
	//导致子类如果有堆区属性,就会出现内存泄露情况
	//解决方法 把析构改为虚析构
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

 纯虚析构函数

#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
	animal()
	{
		cout << "animal的构造函数调用" << endl;
	}
	/*virtual ~animal()
	{
		cout << "animal的析构函数调用" << endl;
	}*/
	virtual ~animal() = 0;
	//纯虚析构 需要声明也需要实现
	//有了纯虚析构,这个类也属于抽象类,无法实例化对象

	virtual void speak() = 0;
};
animal::~animal()
{
	cout << "animal的纯虚析构函数调用" << endl;
}
class cat:public animal
{
public:
	cat(string name)
	{
		cout << "cat的构造函数调用" << endl;
		m_name = new string(name);
	}
	virtual void speak()
	{
		cout << *m_name<<"小猫在说话" << endl;
	}
	~cat()
	{
		cout << "cat析构函数调用" << endl;
		if (m_name != NULL) delete m_name, m_name = NULL;
	}
	string* m_name;
};
void test01()
{
	animal* animal = new cat("tom");
	animal->speak();
	//父类指针在析构的时候,不会调用子类中析构函数
	//导致子类如果有堆区属性,就会出现内存泄露情况
	//解决方法 把析构改为虚析构
	delete animal;
}
int main()
{
	test01();
	system("pause");
	return 0;
}

案例3:电脑组装

电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装成抽象基类,并且提供不同的厂商生产不同的零件

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作。

懒得敲了,这里参考了:C++多态案例:电脑组装_wangpailiulanqi8的博客-CSDN博客

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

 
//抽象零件类
class CPU 
{
public:
	virtual void Calculator() = 0; 
 
};
 
class VideoCard //显卡
{
public:
	virtual void Display() = 0;
 
};
 
class MemoryBank
{
public:
	virtual void Storage() = 0;
 
};
 
 
//电脑类 :需要调用各个零件的接口
class Computer
{
public:
	//接收指针
	Computer(CPU* cpu, VideoCard* video, MemoryBank* memory)
	{
		m_cpu = cpu;
		m_video = video;
		m_memory = memory;
	}
 
	//使用接口指针,提供工作的函数
	void Work()
	{
		m_cpu->Calculator();
		m_video->Display();
		m_memory->Storage();
	}
 
	//释放指针
	~Computer()
	{
		cout << "computer 析构函数" << endl<<endl;
		if (m_cpu != NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_video != NULL)
		{
			delete m_video;
			m_video = NULL;
		}
		if (m_memory != NULL)
		{
			delete m_memory;
			m_memory = NULL;
		}
	}
 
private:
	CPU* m_cpu;
	VideoCard* m_video;
	MemoryBank* m_memory;
 
};
 
//零件的子类
//1、Intel厂商
class IntelCPU :public CPU
{
public:
	void Calculator()
	{
		cout << "Intel的CPU在计算" << endl;
	}
};
 
 
class IntelVideoCard :public VideoCard
{
public:
	void Display()
	{
		cout << "Intel的显卡在显示" << endl;
	}
};
 
class IntelMemoryBank :public MemoryBank
{
public:
	void Storage()
	{
		cout << "Intel的内存在存储" << endl;
	}
};
 
 
//2、Lenovo厂商
class LenovoCPU :public CPU
{
public:
	void Calculator()
	{
		cout << "Lenovo的CPU在计算" << endl;
	}
};
 
 
class LenovoVideoCard :public VideoCard
{
public:
	void Display()
	{
		cout << "Lenovo的显卡在显示" << endl;
	}
};
 
class LenovoMemoryBank :public MemoryBank
{
public:
	void Storage()
	{
		cout << "Lenovo的内存在存储" << endl;
	}
};
 
 
 
 
 
//测试函数
void test01()
{
	//第一台电脑的零件
	CPU* cpu01 = new IntelCPU;
	VideoCard* video01 = new IntelVideoCard;
	MemoryBank* memory01 = new IntelMemoryBank;
 
	//创建第一台电脑
	//使用new创建对象,必须准备一个指针来接收
	Computer *computer01 = new Computer(cpu01, video01, memory01);
	computer01->Work();
	delete computer01;
 
 
	//创建第二台电脑
	Computer* computer02 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemoryBank);
	computer02->Work();
	delete computer02;
 
 
	//创建第三台电脑
	Computer* computer03 = new Computer(new LenovoCPU, new IntelVideoCard, new IntelMemoryBank);
	computer03->Work();
	delete computer03;
 
}
 
int main()
{
	test01();
	system("pause");
	return 0;
}

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

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

相关文章

Python |浅谈爬虫的由来

本文概要 本篇文章主要介绍Python爬虫的由来以及过程&#xff0c;适合刚入门爬虫的同学&#xff0c;文中描述和代码示例很详细&#xff0c;干货满满&#xff0c;感兴趣的小伙伴快来一起学习吧&#xff01; &#x1f31f;&#x1f31f;&#x1f31f;个人简介&#x1f31f;&…

快速搭建一个spring入门案例及整合日志

目录 环境要求 构建模块 程序开发 引入依赖 创建java类 创建配置文件 创建测试类测试 运行测试程序 程序分析 spring中配置启用Log4j2日志框架 Log4j2日志概述 引入Log4j2依赖 加入日志配置文件 测试 使用日志 环境要求 JDK&#xff1a;Java17&#xff08;Spring6要…

Linux高性能服务器编程|阅读笔记:第6章 - 高级I/O函数

目录 简介6.1 pipe函数6.2 dup函数和dup2函数6.3 readv函数和writev函数6.4 sendfile函数6.5 mmap函数和munmap函数6.6 splice函数6.7 tee函数6.8 fcntl函数结语 简介 Hello&#xff01; 非常感谢您阅读海轰的文章&#xff0c;倘若文中有错误的地方&#xff0c;欢迎您指出&…

10-Vue技术栈之脚手架配置代理(解决跨域问题)+ GitHub用户搜索案例

目录 1、基本使用1.1 方法一1.2 方法二 2、GitHub用户搜索案例 1、基本使用 1.1 方法一 ​ 在vue.config.js中添加如下配置&#xff1a; devServer:{proxy:"http://localhost:5000" }说明&#xff1a; 优点&#xff1a;配置简单&#xff0c;请求资源时直接发给前…

用三角函数解决几何问题

如图&#xff0c;在 △ A B C \triangle ABC △ABC 中&#xff0c; A C > 5 , A B > A C AC>5,AB>AC AC>5,AB>AC&#xff0c;点 E E E 是 A B AB AB 上一点&#xff0c;链接 C E CE CE&#xff0c;将 △ B C E \triangle BCE △BCE 沿 C E CE CE 折叠&…

【unity之数据持久化】-Unity公共类PlayerPrefs

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

FreeRTOS 内存管理

文章目录 一、FreeRTOS 内存管理简介二、 内存碎片三、heap_1 内存分配方法1. 分配方法简介2. 内存申请函数详解3. 内存释放函数详解 四、heap_2 内存分配方法1. 分配方法简介2. 内存块详解3. 内存堆初始化函数详解4. 内存块插入函数详解5. 内存申请函数详解6. 内存释放函数详解…

操作系统考试复习——第四章 4.3连续分配存储管理方式

在这里的开头需要讲述一下碎片&#xff0c;碎片分为内碎片和外碎片两种。 内碎片&#xff1a;分区之内未被利用的空间外碎片&#xff1a;分区之间难以利用的空闲分区&#xff08;通常是小空闲分区&#xff09;。 连续分配存储管理方式: 为了能将用户程序装入内存&#xff0c…

力扣刷题Day12_2

144.二叉树的前序遍历 测试代码main() class TreeNode:def __init__(self, valNone, leftNone, rightNone):self.val valself.left leftself.right rightfrom typing import Listclass Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:s Solution…

C++易错编程练习题(1)

0 编程练习 基础不牢靠&#xff0c;回头来补课。小白这个系列主要是为了重新打基础&#xff0c;为一些常见的易错编程练习题提供记录。其间若有错误&#xff0c;欢迎指出&#xff0c;轻喷勿骂。毕竟小白确实是基础不牢靠。 1 题目 自定义函数之整数处理。 题目描述 输入10个…

多视图局部共现和全局一致性学习提高乳腺图像分类的综合性

文章目录 Multi-view Local Co-occurrence and Global Consistency Learning Improve Mammogram Classification Generalisation摘要本文方法global consistency modulelocal co-occurrence module (LCM) 实验结果 Multi-view Local Co-occurrence and Global Consistency Lear…

okio篇3-超时机制

关于System.nanoTime System.currentTimeMills与System.nanoTime实际都是时间间隔&#xff0c;只不过两个时间的起始时间衡量不一致。 我们比较常用的&#xff0c;实际是System.currentTimeMills()&#xff0c;这个时间是以1970-01-01起始&#xff0c;到系统显示时间的间隔。…

聚浪成潮,网易数帆CodeWave智能开发平台开启低代码新时代

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 随着全球范围内新一代人工智能技术发展突飞猛进&#xff0c;社会各领域从数字化、网络化向智能化转变&#xff0c;如何进一步释放数据生产力、加速智能化转型已成为企业发展的必修课。 2023年4月25日&#xff0c;“网易数帆…

K8S管理系统项目实战[API开发]-2

后端: gogin 后端代码地址GitHub - yunixiangfeng/k8s-platform: K8s管理系统后端: gogin 5、存储与配置 5.1 ConfigMap 5.2 Secret 5.3 PersistentVolumeClaims 6、工作流 6.1 流程设计 6.2 数据库操作&#xff08;GORM&#xff09; &#xff08;1&#xff09;初始化…

Mysql Sharding-JDBC读写分离 原理

0 课程视频 深入Sharding-JDBC分库分表从入门到精通【黑马程序员】_哔哩哔哩_bilibili 1 基本概念 1.1应用逻辑 1.1.1 msyql 多库 多表 多服务器 1.1.2 通过Sharding-JDBC jar包->增强JDBC 访问多数据源 -> 自动处理成一个数据源 1.1.3 使用数据的人 -> 使用Sh…

Java面试题总结 | Java面试题总结12- 测试模块

测试 测试需要具备的素质 基础的理论知识、编程语言的功底、自动化测试工具、计算机基础知识 业务分析能力&#xff1a;分析业务的流程&#xff0c;分析被测业务数据、分析被测系统的框架、分析业务模块、分析测试所需资源、分析测试完成目标 缺陷洞察能力&#xff1a;一般…

【ChatGPT】吴恩达教程笔记(预备篇)

本文概要 众所周知&#xff0c;吴恩达老师与OpenAI联合推出了一门面向开发者的Prompt课程&#xff08;https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers &#xff09;&#xff0c;时隔几天&#xff0c;吴恩达老师发推说已经有超过20万人…

Netty基础

2.1Netty是什么 是一个基于异步的&#xff08;多线程处理结果和接收&#xff09;、事件驱动的网络应用框架&#xff0c;用于基于快速开发可维护、高性能的网络服务器和客户端 异步是指调用时的异步&#xff0c;他的IO还是多路复用的IO 许多中间件都依赖与Netty zookperhado…

GUITAR PRO8吉他软件好不好用?值不值得下载

所谓“工欲善其事&#xff0c;必先利其器”&#xff0c;想成为一名专业甚至著名的音乐人&#xff0c;用到的工具软件非常多&#xff0c;在众多款软件工具中&#xff0c;Guitar Pro 8能满足乐谱创作者、学习者的所有需要。很多人在听到Guitar Pro这个名词时&#xff0c;本能反应…

spark的RDD算子计算

一、环境配置 import osfrom pyspark import SparkConf, SparkContextif __name__ __main__:os.environ[SPARK_HOME] /export/server/sparkos.environ["PYSPARK_PYTHON"] "/root/anaconda3/envs/pyspark_env/bin/python"os.environ["PYSPARK_DRIV…