从0到1入门C++编程——06 类和对象之多态、文件操作
文章目录
- 多态
- 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;elsecout<<"除数不能为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入门编程,学习编程不再难
相关文章:

从0到1入门C++编程——06 类和对象之多态、文件操作
文章目录 多态1.多态基本概念2.多态案例——计算器3.纯虚函数和抽象类4.多态案例——制作饮品5.虚析构和纯虚析构6.多态案例——电脑组装 文件操作1.文本文件--写文件2.文本文件--读文件3.二进制文件--写文件4.二进制文件--读文件 多态 1.多态基本概念 多态是C面向对象的三大…...

elementUI实现selecttree自定义下拉框树形组件支持多选和搜索
elementUI实现selecttree自定义下拉框树形组件支持多选和搜索 效果图定义子组件父组件应用 效果图 定义子组件 主要结合el-select和el-tree两个组件改造的。 <template><div class"selectTree"><el-select filterable :filter-method"filterMe…...

使用 Spring Boot 构建 Docker 镜像的简易指南
Spring Boot 是一个用于创建独立的、生产级别的 Spring 应用程序的框架。结合 Docker,你可以方便地将你的 Spring Boot 应用程序打包成一个容器镜像,实现更加灵活和可移植的部署。本文将指导你如何使用 Docker 构建一个包含 Spring Boot 应用程序的镜像。…...

「数据结构」3.ArrayList
🎇个人主页:Ice_Sugar_7 🎇所属专栏:Java数据结构 🎇**欢迎点赞收藏加关注哦!* ArrayList 🍉ArrayList的构造🍉add方法🍌扩容机制🍌重要结论 🍉其…...

H.264与H.265的主要差异
H.265仍然采用混合编解码,编解码结构域H.264基本一致, H.265与H.264的主要不同 编码块划分结构:采用CU (CodingUnit)、PU(PredictionUnit)和TU(TransformUnit)的递归结构。 并行工具:增加了Tile以及WPP等并行工具集以提高编码速…...

【数据结构】 归并排序超详解
1.基本思想 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序…...

Debezium系列之:深入理解GTID全局事务标识,并记录一次数据库重启造成数据丢失的原因和解决方案
Debezium系列之:深入理解GTID,并记录一次数据库重启造成数据丢失的原因和解决方案 一、背景二、深入理解什么是GTID三、深入理解gtid的uuid部分四、判断GTID之间的顺序大小五、解决方案一、背景 hive数据库的表与源头业务数据库的数据不一致,经过检查发现源头数据库发生了重…...

格式化内存卡后,如何找回丢失的监控视频?
随着摄像头的应用越来越广泛,很多监控摄像头采用了内存卡作为存储介质,方便用户存储和查看摄像头拍摄的视频文件。然而,由于各种原因,监控摄像头的内存卡有时会被意外格式化导致重要数据的丢失,给用户带来诸多困扰。 那…...

《动手学深度学习(PyTorch版)》笔记4.8
注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过。…...

助力水下潜行:浮力调节系统仿真
01.建设海洋强国 海洋蕴藏着丰富的资源,二十大报告强调,要“发展海洋经济,保护海洋生态环境,加快建设海洋强国”。建设海洋强国旨在通过科技创新驱动、合理开发利用海洋资源、强化海洋环境保护与生态修复、提升海洋经济质量等多个…...

Mysql常用sql语句
1、建表语句 --建表语句 CREATE TABLE students (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),age INT ); 2、插入语句 --插入测试数据 insert into test_2 values(1,zhangsan); 3、查询语句 --查询语句 MySQL [test_drds_2]> select * from test_2; -------…...

dubbo rpc序列化
序列化配置 provider <dubbo:service interface"com.example.DemoService" serialization"hessian2" ref"demoService"/>consumer <dubbo:reference id"demoService" interface"com.example.DemoService" seria…...

【C语言】va_list(可变参数处理)
C 语言中的 va_list 类型允许函数接受可变数量的参数,这在编写需要处理不定数量参数的函数时非常有用。va_list 类型是在 stdarg.h 头文件中定义的,它允许函数处理可变数量的参数。下面我们将详细介绍 va_list 的用法以及实际应用示例。 一、va_list的用…...

负载均衡下的webshell连接
一、环境配置 1.在Ubuntu上配置docker环境 我们选择用Xshell来将环境资源上传到Ubuntu虚拟机上(比较简单) 我们选择在root模式下进行环境配置,先将资源文件复制到root下(如果你一开始就传输到root下就不用理会这个) …...

5-4 D. DS串应用—最长重复子串
题目描述 求串的最长重复子串长度(子串不重叠)。例如:abcaefabcabc的最长重复子串是串abca,长度为4。 输入 测试次数t t个测试串 输入样例: 3 abcaefabcabc szu0123szu szuabcefg 输出 对每个测试串,输出最…...

C语言实现12种排序算法
1.冒泡排序 思路:比较相邻的两个数字,如果前一个数字大,那么就交换两个数字,直到有序。 时间复杂度:O(n^2),稳定性:这是一种稳定的算法。 代码实现: void bubble_sort(int arr[],…...

C语言应用实例——贪吃蛇
(图片由AI生成) 0.贪吃蛇游戏背景 贪吃蛇游戏,最早可以追溯到1976年的“Blockade”游戏,是电子游戏历史上的一个经典。在这款游戏中,玩家操作一个不断增长的蛇,目标是吃掉出现在屏幕上的食物,…...

Mac如何设置一位数密码?
一、问题 Mac如何设置一位数密码? 二、解答 1、打开终端 2、清除全局账户策略 sudo pwpolicy -clearaccountpolicies 输入开机密码,这里是看不见的,输入完回车即可 3、重新设置密码 (1)打开设置-->用户和群组…...

运动编辑学习笔记
目录 跳舞重建: 深度运动重定向 Motion Preprocessing Tool anim_utils MotionBuilder 跳舞重建: https://github.com/Shimingyi/MotioNet 深度运动重定向 https://github.com/DeepMotionEditing/deep-motion-editin 游锋生/deep-motion-editin…...

C#小结:ScottPlot 5.0在VS2022桌面开发的应用(以winform为例)
目录 一、官网文档地址 二、在VS2022中安装Scottplot 三、拖动Scottplot 四、使用Scottplot 五、效果图 一、官网文档地址 官网地址:ScottPlot 5.0 食谱 本文内容来自于官网,选取了官网的一些比较好用的功能展示,如需学习更多功能&a…...

Jmeter性能测试: Jmeter 5.6.3 分布式部署
目录 一、实验 1.环境 2.jmeter 配置 slave 代理压测机 3.jmeter配置master控制器压测机 4.启动slave从节点检查 5.启动master主节点检查 6.运行jmeter 7.观察jmeter-server主从节点变化 二、问题 1.jmeter 中间请求和响应乱码 一、实验 1.环境 (1&#…...

跟着cherno手搓游戏引擎【15】DrawCall的封装
目标: Application.cpp:把渲染循环里的glad代码封装成自己的类: #include"ytpch.h" #include "Application.h"#include"Log.h" #include "YOTO/Renderer/Renderer.h" #include"Input.h"namespace YO…...

Qt实现窗口吸附屏幕边缘 自动收缩
先看效果: N年前的QQ就可以吸附到屏幕边缘,聊天时候非常方便,不用点击状态栏图标即可呼出QQ界面 自己尝试做了一个糙版的屏幕吸附效果。 关键代码: void Widget::mouseMoveEvent(QMouseEvent *e) {int dx e->globalX() - l…...

shell脚本之免交互
目录 一、Here Document 免交互 1、交互与免交互的概念 2、 Here Document 概述 二、Here Document 应用 1、使用cat命令多行重定向 2、使用tee命令多行重定向 3、使用read命令多行重定向 4、使用wc -l统计行数 5、使用passwd命令用户修改密码 6、Here Document 变量…...

Ajax入门与使用
目录 ◆ AJAX 概念和 axios 使用 什么是 AJAX? 怎么发送 AJAX 请求? 如何使用axios axios 函数的基本结构 axios 函数的使用场景 1 没有参数的情况 2 使用params参数传参的情况 3 使用data参数来处理请求体的数据 4 上传图片等二进制的情况…...

蓝桥杯备战——11.NE555测频
1.分析原理图 我们可以看到,上图就是一个NE555构建的方波发生电路,输出方波频率1.44/2(R8Rb3)C,如果有不懂NE555内部结构,工作原理的,可以到B站学习。实在不懂仿真也行,比如我下面就是仿真结果: 然后就是下…...

代码随想录算法训练营第三十三天|509. 斐波那契数 ,● 70. 爬楼梯 , 746. 使用最小花费爬楼梯
确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 代码随想录 视频:从此再也不怕动态规划了,动态规划解题方法论大曝光 !| 理论基础 |力扣刷题总结| 动态规划入门_哔哩哔哩…...

Node.js 文件系统操作指南
文章目录 Node.js 文件系统操作完全指南一、引言二、基本文件操作2.1 读取文件2.2 写入文件2.3 追加内容到文件 三、文件与目录的创建与删除3.1 创建文件3.2 创建目录3.3 删除文件3.4 删除目录 四、文件与目录的信息查询4.1 检查文件或目录是否存在4.2 获取文件信息4.3 获取目录…...

Kotlin 协程1:深入理解withContext
Kotlin 协程1:深入理解withContext 引言 在现代编程中,异步编程已经变得非常重要。在 Kotlin 中,协程提供了一种优雅和高效的方式来处理异步编程和并发。在这篇文章中,我们将深入探讨 Kotlin 协程中的一个重要函数:wi…...

(自用)learnOpenGL学习总结-高级OpenGL-几何着色器
在顶点着色器和片段着色器中间还有一个几何着色器。 几何着色器的输入是一个图元的一组顶点,在几何着色器中进行任意变换之后再给片段着色器,可以变成完全不一样的图元、可以生成更多的顶点。 #version 330 core layout (points) in; layout (line_str…...