从0到1入门C++编程——06 类和对象之多态、文件操作

news2024/12/26 13:06:24

文章目录

  • 多态
    • 1.多态基本概念
    • 2.多态案例——计算器
    • 3.纯虚函数和抽象类
    • 4.多态案例——制作饮品
    • 5.虚析构和纯虚析构
    • 6.多态案例——电脑组装
  • 文件操作
    • 1.文本文件--写文件
    • 2.文本文件--读文件
    • 3.二进制文件--写文件
    • 4.二进制文件--读文件

多态

1.多态基本概念

多态是C++面向对象的三大特性之一。
多态分为静态多态和动态多态,静态多态包括函数重载和运算符重载等,动态多态是指派生类和虚函数实现运行时多态。
静态多态和动态多态的区别:静态多态的函数地址是早绑定的,其在编译阶段就已经确定了函数的地址;动态多态的函数地址是晚绑定的,其在运行阶段才确定函数地址。
函数重写:父类和子类中函数返回值类型、函数名、参数列表完全相同。
动态多态需要满足的条件:有继承关系;子类重写父类的虚函数。
动态多态使用时,父类的指针或引用指向子类的对象。
子类重写父类虚函数,虚函数表指针会将父类的函数表指针替换成本类的;如果不重写父类虚函数,子类将继承父类函数表指针,其指向的函数地址仍是父类的。
多态的简单代码示例如下。

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void speak()   //通过虚函数实现地址晚绑定,vfptr是一个虚函数表指针
	{
		cout<<"Animal speak()"<<endl;
	}
};

class Cat : public Animal
{
public:
	void speak()  //子类重写父类虚函数,虚函数表指针会将父类的替换成本类的
	{
		cout<<"Cat speak()"<<endl;
	}
};

class Dog : public Animal
{
public:
	void speak()
	{
		cout<<"Dog speak()"<<endl;
	}
};

void dospeak(Animal &animal)  //父类的引用可以指向子类对象
{
	animal.speak();
}

void fun()
{
	Cat cat;
	cat.speak();
	dospeak(cat);  //传哪个对象就执行这个对象所在类的函数
	Dog dog;
	dospeak(dog);
}

int main()
{
	fun();

	system("pause");
	return 0;
}

上面代码的执行结果如下图所示。
在这里插入图片描述

2.多态案例——计算器

多态的优点:代码组织结构清晰;可读性强;利于前期和后期的扩展及维护。
利用普通方法实现计算器的代码如下。

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

class Calculator
{
public:
	int getResult(string str)
	{
		if(str == "+")
			return num1+num2;
		else if(str == "-")
			return num1-num2;
		else if(str == "*")
			return num1*num2;
		else if(str == "/")
		{
			if(num2 != 0)
				return num1/num2;
			else
				cout<<"除数不能为0!"<<endl;
		}
	}
	int num1;
	int num2;
};

void fun()
{
	Calculator c;
	c.num1 = 10;
	c.num2 = 20;
	cout<<c.num1<<"+"<<c.num2<<"="<<c.getResult("+")<<endl;
	cout<<c.num1<<"-"<<c.num2<<"="<<c.getResult("-")<<endl;
	cout<<c.num1<<"*"<<c.num2<<"="<<c.getResult("*")<<endl;
	cout<<c.num1<<"/"<<c.num2<<"="<<c.getResult("/")<<endl;
}

int main()
{
	fun();
	
	system("pause");
	return 0;
}

利用多态方法实现计算器的代码如下。

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

//计算器的抽象类
class AbstractCalculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int num1;
	int num2;
};

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

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

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

//除法计算器类
class DivCalculator : public AbstractCalculator
{
public:
	int getResult()
	{
		return num1/num2;
	}
};

void fun()
{
	//指针方式
	AbstractCalculator *c = new AddCalculator;
	c->num1 = 10;
	c->num2 = 20;
	cout<<c->num1<<"+"<<c->num2<<"="<<c->getResult()<<endl;
	delete c;
	c = new SubCalculator;
	c->num1 = 10;
	c->num2 = 20;
	cout<<c->num1<<"-"<<c->num2<<"="<<c->getResult()<<endl;
	delete c;
	c = new MultiCalculator;
	c->num1 = 10;
	c->num2 = 20;
	cout<<c->num1<<"*"<<c->num2<<"="<<c->getResult()<<endl;
	delete c;
	c = new DivCalculator;
	c->num1 = 10;
	c->num2 = 20;
	cout<<c->num1<<"/"<<c->num2<<"="<<c->getResult()<<endl;
	delete c;
	//引用方式
	AddCalculator add;
	AbstractCalculator &a = add;
	add.num1 = 10;
	add.num2 = 20;
	cout<<add.num1<<"*"<<add.num2<<"="<<add.getResult()<<endl;
}

int main()
{
	fun();
	
	system("pause");
	return 0;
}

上面代码的运行结果如下图所示。
在这里插入图片描述
利用多态方法的代码量相比于普通代码增多了,但是代码组织结构更加清晰,可读性也更强,利于扩展和维护。

3.纯虚函数和抽象类

在多态中,通常父类的虚函数实现是无意义的,调用的一般是子类重写后的函数。因此可以将父类的虚函数替换为纯虚函数。
纯虚函数的语法:virtual 返回值类型 函数名 (参数列表) = 0;
当类中有了纯虚函数,类就被称为抽象类。
抽象类的特点是:无法实例化对象,栈区和堆区都是不可以的;子类必须重写父类中的纯虚函数,否则其也属于抽象类。
纯虚函数和抽象类的例子如下。

#include <iostream>
#include <string>

using namespace std;

class Parent 
{
public:
	virtual void fun() = 0;  //纯虚函数
	//Parent类就是抽象类,无法实例化对象
};

class Son1 : public Parent
{
public:
	void fun()  //重写父类的纯虚函数
	{
		cout<<"Son1类的fun()调用!"<<endl;
	}
};

class Son2 : public Parent
{
public:
	void fun()  //重写父类的纯虚函数
	{
		cout<<"Son2类的fun()调用!"<<endl;
	}
};

int main()
{
	Parent *p1 = new Son1;   //创建对象
	p1->fun();   //创建的对象不同,访问的函数也不同
	delete p1;
	Parent *p2 = new Son2;
	p2->fun();
	delete p2;
	system("pause");
	return 0;
}

程序的运行结果如下图所示。
在这里插入图片描述
多态的目的就是让函数的接口更加通用化。

4.多态案例——制作饮品

制作饮品的流程:煮水、冲泡、倒入杯中、加入辅料。
利用多态技术实现制作饮品,提供抽象类制作饮品基类,提供子类制作咖啡和茶。
该案例的代码实现如下。

#include <iostream>
#include <string>

using namespace std;

class AbstractDrinking   //抽象类
{
public:
	virtual void Boil() = 0;  //1煮水
	virtual void Brew() = 0;  //2冲泡
	virtual void PourInCup() = 0;  //3倒入杯中
	virtual void PutOthers() = 0;  //4加入辅料
	void makeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PutOthers();
	}
};

class Tea : public AbstractDrinking
{
public:
	Tea()
	{
		cout<<"制作茶的步骤:"<<endl;
	}
	//依次重写父类中的纯虚函数
	void Boil()
	{
		cout<<"1.煮水"<<endl;
	}
	void Brew()
	{
		cout<<"2.冲泡茶叶"<<endl;
	}
	void PourInCup()
	{
		cout<<"3.倒入茶杯"<<endl;
	}
	void PutOthers()
	{
		cout<<"4.加入枸杞和桂圆"<<endl;
	}
};

class Coffee : public AbstractDrinking
{
public:
	Coffee()
	{
		cout<<"制作咖啡的步骤:"<<endl;
	}
	//依次重写父类中的纯虚函数
	void Boil()
	{
		cout<<"1.煮水"<<endl;
	}
	void Brew()
	{
		cout<<"2.冲泡咖啡"<<endl;
	}
	void PourInCup()
	{
		cout<<"3.倒入咖啡杯中"<<endl;
	}
	void PutOthers()
	{
		cout<<"4.加入糖和牛奶"<<endl;
	}
};

void fun(AbstractDrinking *p)
{
	p->makeDrink();
	delete  p;
}

int main()
{
	fun(new Tea);
	cout<<"----------------------"<<endl;
	fun(new Coffee);
	system("pause");
	return 0;
}

程序的运行结果如下图所示。
在这里插入图片描述

5.虚析构和纯虚析构

多态在使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类中的析构代码。因此,要将父类中的析构函数改为虚析构或者纯虚析构。
虚析构和纯虚析构的共性:可以解决父类中指针释放子类对象问题;都需要具体的函数实现。
如果子类中没有堆区数据,可以不写虚析构或者纯虚析构。
如果类中是纯虚析构,该类是抽象类,无法实例化对象。
虚析构的语法:virtual ~类名(){}
纯虚析构的语法:virtual ~类名()=0;类名:: ~类名(){}

虚析构代码的代码实现例子如下。

#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 catName)
	{
		cout<<"Cat类构造函数"<<endl;
		name = new string(catName);
	}
	void speak()
	{
		cout<<*name<<" is speaking."<<endl;
	}
	~Cat()   //析构函数中释放堆区内存
	{
		cout<<"Cat类析构函数"<<endl;
		if(name != NULL)
		{
			delete name;
			name = NULL;
		}
	}
	string *name;
};

void fun()
{
	Animal *p = new Cat("Tom");
	p->speak();
	delete p;
}

int main()
{
	fun();

	system("pause");
	return 0;
}

上面程序中如果在父类中不使用虚析构,其运行结果如下图所示,可以看到,父类指针在析构的时候无法执行子类的析构函数,后果就是如果子类中有堆区数据属性,就会产生内存泄漏。
在这里插入图片描述
父类中使用虚析构,其运行结果如下图所示,子类中的析构函数也被执行了。
在这里插入图片描述
纯虚函数在父类中只做声明,不做实现。不同与纯虚函数,纯虚析构在父类中不仅需要有声明,还需要有具体的实现,因为父类中也可能有一些属性开辟在堆区。
在上面代码的基础上,需要修改为纯虚析构代码的部分如下。

class Animal
{
public:
	Animal()
	{
		cout<<"Animal类构造函数"<<endl;
	}
	virtual ~Animal() = 0;   //纯虚析构的声明
	virtual void speak() = 0;  //纯虚函数
};

Animal :: ~Animal()   //纯虚析构的实现
{
	cout<<"Animal类纯虚析构函数"<<endl;
}

使用纯虚析构后的程序运行结果如下图所示。
在这里插入图片描述

6.多态案例——电脑组装

电脑的主要组成部件有CPU、显卡、内存。将每个部件封装出抽象的基类,并提供不同的厂商生产不同的零件,创建电脑类提供让电脑工作的函数,并调用每个部件工作的接口。
该案例的代码实现如下。

#include <iostream>
#include <string>

using namespace std;

class CPU   //CPU抽象类
{
public:
	virtual void calculate() = 0;  //用于计算的纯虚函数
};

class VideoCard   //显卡抽象类
{
public:
	virtual void display() = 0;  //用于显示的纯虚函数
};

class Memory   //内存抽象类
{
public:
	virtual void storage() = 0;  //用于存储的纯虚函数
};

class Computer   //电脑类
{
public:
	Computer(CPU *cpu,VideoCard *vc,Memory *m)
	{
		this->cpu = cpu;  //构造函数中接收各指针
		this->vc = vc;
		this->m = m;
	}
	void work()
	{
		cpu->calculate();   //调用各零件接口
		vc->display();
		m->storage();
	}
	~Computer()  //析构函数释放指针
	{
		if(cpu != NULL)
		{
			delete cpu;
			cpu = NULL;
		}
		if(vc != NULL)
		{
			delete vc;
			vc = NULL;
		}
		if(m != NULL)
		{
			delete m;
			m = NULL;
		}
	}
private:
	CPU *cpu;  //CPU零件指针
	VideoCard *vc;
	Memory *m;
};

//Intel厂商
class IntelCPU : public CPU
{
public:
	void calculate()
	{
		cout<<"Intel_CPU_calculate()"<<endl;
	}
};

class IntelVideoCard : public VideoCard
{
public:
	void display()
	{
		cout<<"Intel_VideoCard_display()"<<endl;
	}
};

class IntelMemory : public Memory
{
public:
	void storage()
	{
		cout<<"Intel_Memory_storage()"<<endl;
	}
};

//Lenovo厂商
class LenovoCPU : public CPU
{
public:
	void calculate()
	{
		cout<<"Lenovo_CPU_calculate()"<<endl;
	}
};

class LenovoVideoCard : public VideoCard
{
public:
	void display()
	{
		cout<<"Lenovo_VideoCard_display()"<<endl;
	}
};

class LenovoMemory : public Memory
{
public:
	void storage()
	{
		cout<<"Lenovo_Memory_storage()"<<endl;
	}
};

void test()
{
	cout<<"第一台电脑组装:"<<endl;
	CPU *icpu = new IntelCPU;
	VideoCard *ivc = new IntelVideoCard;
	Memory *m = new IntelMemory;
	Computer *c1 = new Computer(icpu,ivc,m);
	c1->work();
	delete c1;
	cout<<"----------------------"<<endl;
	cout<<"第二台电脑组装:"<<endl;
	Computer *c2 = new Computer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
	c2->work();
	delete c2;
	cout<<"----------------------"<<endl;
	cout<<"第三台电脑组装:"<<endl;
	Computer *c3 = new Computer(new LenovoCPU,new IntelVideoCard,new LenovoMemory);
	c3->work();
	delete c3;
}

int main()
{
	test();
	system("pause");
	return 0;
}

程序运行结果如下图所示。
在这里插入图片描述


文件操作

程序运行时产生的数据都属于临时数据,程序运行结束后都会被释放,通过文件可以将数据持久化。
C++中对文件的操作需要包含头文件。
文件类型分为两种,文本文件和二进制文件。文本文件以文本的ASCII码形式存储。二进制文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们。
操作文件的三大类:ofstream(写操作)、ifstream(读操作)、fstream(读写操作)。

文件打开方式描述
ios::in读文件
ios::out写文件
ios::ate初始位置是文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

文件的打开方式可以配合使用,如果有多个中间使用 | 符连接。例如用二进制方式写文件 ios::binary | ios::out。

1.文本文件–写文件

写文件的步骤:1.包含头文件 #include ;2.创建流对象 ofstream ofs;3.打开文件 ofs.open(“文件路径”,打开方式);4.写数据 ofs << “写入的数据”(cout是往屏幕上输出,ofs是往文件中输出);5.关闭文件 ofs.close()。
往文本文件中写内容的简单代码如下。

#include <iostream>
#include <fstream>  //1.包含头文件
#include <string>
using namespace std;

void test()
{
	ofstream ofs;  //2.创建流对象
	ofs.open("test.txt",ios::out);   //3.打开文件
	ofs << "This is the first line." <<endl;   //4.往文件写内容
	ofs << "This is the second line." <<endl;
	ofs << "This is the third line." <<endl;
	ofs.close();    //5.关闭文件
}

int main()
{
	test();
	system("pause");
	return 0;
}

程序运行结束后,右键Visual Studio中的cpp文件,选择打开所在的文件夹,在该文件夹下就生成了指定的文件,打开后其内容如下图所示。
在这里插入图片描述
可以看到,文件名就是我们在程序中指定的,当然程序中也可以将文件名指定为绝对路径,如果只是给出文件名,生成的文件默认就在项目所在目录下。文件中的内容也是通过代码写入的。

2.文本文件–读文件

读文件的步骤:1.包含头文件 #include ;2.创建流对象 ifstream ifs;3.打开文件并判断文件是否打开成功 ifs.open(“文件路径”,打开方式),is_open()函数判断文件是否打开成功;4.读数据,共有四种方式;5.关闭文件 ifs.close()。
从文本文件中读取内容的简单代码如下。

#include <iostream>
#include <fstream>  //1.包含头文件
#include <string>
using namespace std;

void test()
{
	ifstream ifs;  //2.创建流对象
	ifs.open("test.txt",ios::in);   //3.打开文件并判断文件是否打开成功
	if(!ifs.is_open())
	{
		cout<<"文件打开失败!"<<endl;
		return;
	}
	//4.读文件内容
	//char buf[1024] = {0}; //方式一读取 —— 读取到数组中
	//while(ifs >> buf)   
	//{
	//	cout <<buf<<endl;
	//}

	//char buf[1024] = {0}; //方式二读取 —— 通过函数逐行读取到数组中
	//while(ifs.getline(buf,sizeof(buf))) 
	//{
	//	cout <<buf<<endl;
	//}

	string buf;  //方式三读取 —— 通过函数逐行读取到字符串中
	while(getline(ifs,buf))
	{
		cout <<buf<<endl;
	}

	//char c;  //方式四读取 —— 逐个字符读取
	//while((c=ifs.get())!=EOF)
	//{
	//	cout<<c;
	//}

	ifs.close();    //5.关闭文件
}

int main()
{
	test();
	system("pause");
	return 0;
}

读取到的文件内容就是上面写入的内容,读取的结果如下图所示。
在这里插入图片描述

3.二进制文件–写文件

以二进制方式往文件中写内容的简单代码如下。

#include <iostream>
#include <fstream>  //1.包含头文件
#include <string>
using namespace std;

class Person
{
public:
	char name[64];
	int age;
};

void test()
{
	ofstream ofs;  //2.创建流对象
	ofs.open("person.txt",ios::out|ios::binary);   //3.以二进制方式打开文件
	//ofstream ofs("person.txt",ios::out|ios::binary);   //2和3步骤也可以用本行代替
	Person p = {"张三",22};
	ofs.write((const char*)&p,sizeof(Person));
	ofs.close();    //5.关闭文件
}

int main()
{
	test();
	system("pause");
	return 0;
}

以二进制方式写的时候可以写入类这种类型的数据到文件,程序运行之后同样在项目文件夹下可以看到新出现的文件,打开后其内容如下图所示。
在这里插入图片描述
可以看到,以二进制写入文件后会出现乱码,有时候甚至都看不懂,不过没关系,只要确保写入的内容是对的,后面通过读取文件就可以验证。

4.二进制文件–读文件

以二进制方式读取文件内容的简单代码如下。

#include <iostream>
#include <fstream>  //1.包含头文件
#include <string>
using namespace std;

class Person
{
public:
	char name[64];
	int age;
};

void test()
{
	ifstream ifs;  //2.创建流对象
	ifs.open("person.txt",ios::in|ios::binary);   //3.以二进制方式打开文件
	if(!ifs.is_open())   //判断文件是否打开成功
	{
		cout<<"文件打开失败!"<<endl;
		return;
	}
	Person p;
	ifs.read((char*)&p,sizeof(Person));
	cout<<"姓名:"<<p.name<<" 年龄:"<<p.age<<endl;
	ifs.close();    //5.关闭文件
}

int main()
{
	test();
	system("pause");
	return 0;
}

程序运行后的结果如下图所示。
在这里插入图片描述
可以看到,虽然以二进制方式写入文件后会出现乱码,但是以二进制方式读取到的内容与写入的内容是一致的。


本文参考视频:
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

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

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

相关文章

RHEL 9上创建本地Yum/DNF存储库

1.挂载RHEL9 ISO文件或DVD 我们假设RHEL 9 iso文件已经复制到系统中。运行以下mount 命令将 ISO文件挂载到/var/repo文件夹。 $ sudo mkdir /var/repo $ sudo mount -o loop rhel-baseos-9.0-x86_64-dvd.iso /var/repo/ 如果是 DVD,请运行 $ sudo mount /dev/sr0 /var/repo/…

系统架构设计师-22年-下午答案

系统架构设计师-22年-下午答案 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答&#xff0c;二、三、四、五题中任选两题作答 试题一 (25分) 说明 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。…

代码随想录 Leetcode669. 修剪二叉搜索树

题目&#xff1a; 代码(首刷看解析 2024年1月31日&#xff09;&#xff1a; class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (!root) return root;if (root->val < low) {TreeNode* node trimBST(root->right,low,high);return…

【Linux】yum与vim命令详解

&#x1f497;个人主页&#x1f497; ⭐个人专栏——Linux学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读1. yum命令1.1 基本使用1.2 注意事项1.3 lrzsz软件包示例 2. vim命令2.1 vim的基本概念2.2 vim配置2.3 vim的基本操作2.3…

远程电脑解决突然无法的复制粘贴问题

方法一 - 重新启动RDP剪贴板 结束掉剪切板服务 创建新任务rdplclip.exe任务 重新复制粘贴&#xff0c;即可解决 方法二&#xff1a; 重新启动 Windos 资源管理器 方法三&#xff1a;设置远程桌面连接的本地资源配置&#xff0c;勾选剪切板服务&#xff0c;并在详细信息中勾选…

如何用 python +ddt+excel 实现接口自动化测试

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 “ 接口自动化测试是指通过编写代码或使用工具&#xff0c;模拟…

计算机网络——网络层(3)

计算机网络——网络层&#xff08;3&#xff09; 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)1 网络层——控制平面因特网中自治系统内部的路由选择总括考虑因素总结 ISP之间的路由选择&#xff1a;BGP考虑因素总结 SDN控制层面重要组件和功能总结 ICMP主要功能和特点…

Mysql单行函数练习

数据表 链接&#xff1a;https://pan.baidu.com/s/1dPitBSxLznogqsbfwmih2Q 提取码&#xff1a;b0rp --来自百度网盘超级会员V5的分享 单行函数练习 单行函数(一行数据返回一个结果) #1.显示系统时间(注:日期时间) #2.查询员工工号,姓名,工资以及提高百分之20后的结果(new…

消费观念升级,品牌又有哪些新的营销玩法?

一方面在传统消费升级的背景下&#xff0c;国民的消费预期不断改善&#xff0c;购买力持续增强&#xff0c;另一方面&#xff0c;随着Z时代成为消费时代的主力军&#xff0c;他们的需求推动了消费行业向个性化、细分化、多样化的方向发展。那在这一消费环境下&#xff0c;品牌又…

深入解析企业培训教育系统开发:源码探秘与技术实践

当下&#xff0c;为了提高员工的技能水平、促进团队的协同合作&#xff0c;企业培训教育系统成为了一个不可或缺的组成部分。本篇文章&#xff0c;小编将为大家讲述企业培训教育系统的开发&#xff0c;揭示其源码背后的奥秘以及相关的技术实践。 一、概述 企业培训教育系统通常…

时间序列预测模型实战案例(三)(LSTM)(Python)(深度学习)时间序列预测(包括运行代码以及代码讲解)

目录 引言 LSTM的预测效果图 LSTM机制 了解LSTM的结构 忘记门 输入门 输出门 LSTM的变体 只有忘记门的LSTM单元 独立循环(IndRNN)单元 双向RNN结构(LSTM) 运行代码 代码讲解 引言 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种常用的循环神经网络&a…

本地搭建Plex私人影音网站并结合内网穿透实现公网远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

网址的静态码和活码怎么做?学会在线制作更快捷

现在获取信息大多都是在手机上搜索的&#xff0c;所以将现在很多推广信息的链接都会选择生成二维码之后&#xff0c;通过扫码在手机上获取传递的信息&#xff0c;那么如何将网址制作二维码使用呢&#xff1f;其实方法很简单&#xff0c;接着网址二维码生成器就可以轻松完成在线…

小程序定制开发前,应该考虑些什么?

引言 在移动互联网时代&#xff0c;小程序已经成为许多企业和个人推广业务、提供服务的理想平台。然而&#xff0c;在进行小程序定制开发之前&#xff0c;开发者和业务方需要细致入微地考虑一系列关键因素&#xff0c;以确保最终的小程序既能满足用户需求&#xff0c;又能够顺…

【附安装包】Java/JDK介绍、下载、安装、配置与使用(保姆级教程)

目录 一、概述 1、Java历史 2、Java介绍&#xff08;JDK、JRE、JVM&#xff09; 3、Java运行机制 4、Java跨平台性 5、JDK版本选择 二、下载安装 1、JDK下载 2、JDK安装 三、配置环境变量 四、测试与使用 1、测试 2、使用 一、概述 1、Java历史 Java语言诞生于SU…

R语言韦恩图的绘制

韦恩图是一种用于展示多个集合之间的交集和并集关系的图形工具。它由英国数学家约翰韦恩于1880年首次引入&#xff0c;因此得名为"韦恩图"。韦恩图利用圆形的重叠区域来表示集合之间的交集&#xff0c;而圆形的非重叠区域则表示各个集合的独立部分。 韦恩图通常用于…

项目管理中,如何识别和管理依赖关系?

项目过程中&#xff0c;很少有任务是独立存在的。因此&#xff0c;在整个项目管理过程中查看完整的项目时间表、依赖关系和所需资源尤为重要。这将有助于团队直观地了解项目进展的全貌&#xff0c;并评估何时以及如何分配任务和资源。 如何识别项目依赖关系&#xff1f; 就像…

matlab中的图窗属性和坐标轴的属性

图窗的Position和Outerposition Position 指定窗口的尺寸和窗口在屏幕中的位置。 Outerposition 指定窗口外轮廓的大小和位置。 两者都是用一个4维向量来定义&#xff0c;格式为[左 底 宽 高]。 可通过set函数修改Position和Outerposition&#xff0c;如下&#xff1a;在屏幕左…

小程序能做视频二维码吗?具体的制作步骤有哪些?

当我们用手机拍摄视频之后&#xff0c;想要快速的将视频传播&#xff0c;除了直接群发或者发送到聊天群的方式之外&#xff0c;现在很多人会将视频生成二维码图片之后&#xff0c;将二维码分享给其他人扫码看视频。 选择这种方式的原因在于&#xff0c;现在活码二维码类型可以…

牛客——字符串(尺取法与滑动窗口)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 小N现在有一个字符串S。他把这这个字符串的所有子串都挑了出来。一个S的子串T是合法的&#xff0c;当且仅当T中包含了所有的小写字母。小N希望知道所有的合法的S的子串中&#xff0c…