【C++】C++应用案例-通讯录管理系统

news2024/11/24 10:29:33

目录

一、整体介绍

1.1、需求和目标

1.2、整体功能描述

二、页面及功能描述

2.1 主菜单

2.2 添加联系人菜单

2.3 显示联系人菜单

2.4 修改联系人菜单

2.5 退出功能

三、流程设计

3.1 主流程

3.2 添加操作流程

3.3 显示联系人操作流程

3.4 修改联系人操作流程

四、代码设计

4.1 核心思路

4.2 项目文件分类设计

4.2.1 头文件

4.2.2 源文件

4.2.3 代码实现步骤

4.3 代码具体实现

4.3.1 定义头文件

4.3.2 实现主体流程    

4.3.3 绘制菜单

4.3.4 读取键盘输入

4.3.5 实现具体操作功能


一、整体介绍

1.1、需求和目标

        项目的需求,实现一个叫做“通讯录管理”的基于命令行界面的通讯录管理软件。

        我们的目标,是通过项目练习,初步掌握编写软件的方法和技巧。

1.2、整体功能描述

        这个软件相对简单,只需要基于命令行做纯文本的交互,不涉及图形化界面。作为一个记录通讯录软件,主要有三大功能:添加联系人、显示联系人与修改联系人。也就是说,软件需要能够记录联系人的姓名与联系方式等相关信息,并提供查询显示联系人信息的功能,也可对联系人信息进行修改。而且为了让软件有真正的实用价值,需要对数据做持久保存:我们这里只需要存放在一个指定的文本文件中。

        项目采用分级菜单方式,每一级菜单应该有“返回主菜单”功能;主菜单有“退出”选项。

二、页面及功能描述

2.1 主菜单

        运行软件后首先应该显示主菜单。主菜单提供四个选项:“添加联系人”、“显示联系人”、"修改联系人"和“退出”,并在下方提示用户输入1-4的数字,选择不同的功能。

主菜单界面如下:

2.2 添加联系人菜单

        在主菜单页面选择“1”,可以进入记账菜单(二级菜单)。添加联系人菜单提供三个选项:“办公”、“个人”和“退出”;并在下方提示用户按对应的数字进行功能选择。

记账菜单界面如下:

        在添加联系人菜单页面选择“1”,可以记录一条办公类联系人信息。联系人信息包括:类型(办公/个人)、姓名、电话、住址。这里需要提示用户输入联系人的姓名、电话与住址信息,然后显示完成添加。

        类似地,在联系人菜单页面选择“2”,可以记录一条个人类联系人信息。提示用户输入联系人的姓名、电话与住址,然后显示完成添加。

2.3 显示联系人菜单

        在主菜单页面选择“2”,可以进入显示联系人菜单。显示联系人菜单提供四个选项:“全部”、“办公”、“个人”和“退出”;并在下方提示用户按对应的数字进行功能选择。

显示联系人菜单界面如下:

        在显示联系人菜单页面,选择“1”可以显示全部联系人信息;选择“2”可以显示所有办公类联系人;选择“3”可以显示所有个人类联系人。用户选择之后,除了应该显示对应的联系人信息外,还应该对所有联系人个数进行统计汇总。

        对应地,选择“1”之后列出所有联系人,统计的是通讯录的总条数;选择“2”之后列出所有办公类联系人,统计办公类总条数;选择“3”之后列出所有个人类联系人,统计个人类总条数。

2.4 修改联系人菜单

        在主菜单页面选择“3”,可以进入修改联系人菜单。修改联系人菜单提供三个选项:“办公”、“个人”和“退出”;并在下方提示用户按对应的数字进行功能选择。

显示联系人菜单界面如下:

        在修改联系人菜单页面,选择“1”可以修改办公类别联系人信息;选择“2”可以个人类别联系人信息。用户选择之后,根据提示输入要修改的联系人姓名,如没有找到,提示未找到此人;如找到此联系人,输入更改后的姓名、电话与住址,并提示修改成功。

2.5 退出功能

        在主菜单页面,选择“3”可以退出软件。页面将做确认退出的对话提醒:如果用户输入“Y”则退出,输入“N”则返回主菜单页面。

三、流程设计

3.1 主流程

3.2 添加操作流程

3.3 显示联系人操作流程

3.4 修改联系人操作流程

四、代码设计

4.1 核心思路

        通讯录软件处理的数据,就是添加或者修改的“联系人”。每一条联系人数据,都应该包含类型、姓名、电话、住址四部分,这可以构建一个结构体类型 Person来表示。

        而程序运行的开始,应该读取保存在文件中的数据,读取之后应该是一个 Person类型的数组。

        在流程控制方面,如果用户不选择退出,程序就不会结束,所以应该用一个while循环来处理整个流程;当用户确认退出时,更改一个标志位,用来退出循环。

        每一级菜单做键盘选择后,可以用 switch 分支语句来处理;不同的功能模块,可以包装成函数。

4.2 项目文件分类设计

4.2.1 头文件

        对于一个C++项目来说,构建整体架构很重要的一步就是定义头文件。一般我们会把全局变量、函数声明、以及结构体的定义都放在头文件中。

本项目可以设计两个头文件,来管理不同的内容:

  1. 结构体 Person的定义,以及涉及到账目操作的函数声明,都可以放在一个头文件 person_item.h 中;
  2. 其它一些通用的设置和功能性函数,可以放在另一个头文件common.h 中

4.2.2 源文件

真正的代码实现都放在源文件中。根据不同的用途,项目中可以用四个源文件来实现对应的功能

  1. 主体代码可以放在一个源文件 person_main.cpp 中
  2. 绘制菜单的函数都放在menus.cpp中
  3. 通用函数(比如读取键盘输入)放在read_input.cpp中
  4. 针对账目的所有操作函数(比如记账、查询)全部放在operations.cpp中

4.2.3 代码实现步骤

1、实现整体框架和主题流程

  • 在头文件person_item.h中定义好结构体;在common.h 中定义通用配置
  • 在源文件person_main.cpp的主函数中,参照主流程图实现主体流程

2、填充具体功能

  • 在menus.cpp中定义绘制各级菜单的函数;函数声明放在common.h里
  • 在read_input.cpp中定义读取键盘输入的函数,包括读取菜单选择、读取输入的金额数、读取确认退出信息;这些函数都需要对输入做合法性校验,并返回正确的值;函数声明放在common.h里
  • 在operations.cpp中定义从文件加载数据、记账、查询对应的函数。

3、各功能的细分

1)添加功能

  • 首先需要读取键盘输入的姓名、电话和备住址信息;
  • 然后将其保存在一个Person对象中,并添加到vector,同时写入文件

2)显示功能

  • 需要用for循环遍历整个vector
  • 通过条件筛选需要的联系人打印输出,并统计总和

2)修改功能

  • 需要用for循环遍历整个vector
  • 通过条件匹配到联系人,对数据进行更改
  • 将更改后的数组重新写入文件

4)退出功能

  • 提示用户确认退出
  • 用户确认后,更改循环标志位,退出循环

4.3 代码具体实现

4.3.1 定义头文件

        首先可以在common.h中,引入相应的库,定义出需要的全局变量,以及声明绘制菜单和读取键盘输入的通用函数。通用设置,通用功能性函数声明。

#pragma once
//通用设置,通用功能性函数声明
#include<iostream>			//输入输出
#include<string>			//字符串操作
#include<fstream>			//文件操作
#include<vector>
#include <algorithm>
using namespace std;

#define max 1000
#define OFFICE "办公"
#define MYSELF "个人"
#define FILENAME "book.txt"
//绘制菜单函数
void menus_main();
void menus_app();
void menus_show();
void menus_revise();
void menus_delete();

//读取键盘输入进行合法性校验的函数
char readMenuSelection(int options);
char readquitconfirm();

        然后在person_item.h定义核心数据结构:结构体 Person,并声明涉及到账目操作的函数。结构体定义,涉及账目操作的函数声明。

#pragma once
//结构体定义,涉及通讯录管理操作的函数声明
#include "common.h"

//设计联系人结构体
struct Person
{
	string PersonItem;	//类别,1、办公,2、个人
	string name;	//姓名
	string iphone;	//电话
	string address;	//单位
};

//针对通讯录数据进行操作的函数

//加载通讯录数据
void loadDataFromFile(vector<Person>& ps);


//添加
void person_add(vector<Person>& ps);
void insertIntoFile(const Person& p);
void person_addmyself(vector<Person>& ps);
void person_addOffice(vector<Person>& ps);
//显示
void person_show(vector<Person>& ps);
void showPerson(const vector<Person>& ps);
void showPerson(const vector<Person>& ps, const string PersonItem);
void printPerson(const Person& p);
//修改
void person_revise(vector<Person>& ps);
void change(vector<Person>& ps, string item);

4.3.2 实现主体流程    

在源文件 person_main.cpp 的主函数中,参照主流程图实现主体流程。

代码如下:

#include "common.h"
#include "person_item.h"

int main() {
	//从文件加载数据
	vector<Person> ps;
	loadDataFromFile(ps);

	bool quit = false;
	while (!quit)
	{
		//1、显示主菜单
		menus_main();

		//2、读取键盘输入,并做合法性校验
		char key= readMenuSelection(5);
		switch (key)
		{
			
		case '1':			//1、添加联系人
			menus_app();	//添加菜单
			person_add(ps);	//添加功能
			break;
			
		case '2':			//2、显示联系人
			menus_show();	//显示菜单
			person_show(ps);		//显示功能
			break;
			
		case '3':			//3、修改联系人
			menus_revise();	//修改菜单
			person_revise(ps);	//修改功能
			break;
			

		case '4':			//退出
			cout << "确认退出? (Y/N)";
			if (readquitconfirm() == 'Y')
				quit = true;
			break;
		default:
			break;
		}

	}


}

4.3.3 绘制菜单

menus.cpp 中定义绘制各级菜单的函数;对应的函数声明放在 common.h里。

代码如下:

//菜单函数
#include "common.h"

//主菜单
void menus_main() {
	system("cls");		//清屏
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|***************     1 添加联系人     ********************|" << endl;
	cout << "|***************     2 显示联系人     ********************|" << endl;
	cout << "|***************     3 修改联系人     ********************|" << endl;
	cout << "|***************     4 退      出     ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 4):";
}
//添加联系人菜单
void menus_app() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择添加联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
	cin.get();
}
//显示联系人菜单
void menus_show() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择显示联系人类别 ********************|" << endl;
	cout << "|***************   1  全        部    ********************|" << endl;
	cout << "|***************   2  办        公    ********************|" << endl;
	cout << "|***************   3  个        人    ********************|" << endl;
	cout << "|***************   4  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 4):";
}
//修改联系人菜单
void menus_revise() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择修改联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
}
//删除联系人菜单
void menus_delete() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择删除联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
}

4.3.4 读取键盘输入

        在 read_input.cpp 中定义读取键盘输入的函数,包括读取菜单选择、读取输入的金额数、读取确认退出信息;这些函数都需要对输入做合法性校验,并返回正确的值。

代码如下:

//读取输入函数
#include "common.h"

//读取键盘输入的菜单选项,进行合法性校验
char readMenuSelection(int options) {
	string str;

	while (true)
	{
		getline(cin, str);
		//进行合法化校验
		if (str.size() != 1 || str[0] - '0' <= 0 || str[0] - '0' > options)
			cout << "输入错误,请重新选择:";
		else
			break;
	}

	//输入合法
	return str[0];

}


//读取确认退出信息,并进行合法性校验
char readquitconfirm() {
	string str;
	while (true)
	{
		getline(cin, str);
		//进行合法性校验
		if (str.size() != 1 || toupper(str[0]) != 'Y' && toupper(str[0]) != 'N')
			cout << "输入错误,请重新选择:";
		else
			break;
	}
	//输入合法
	return toupper(str[0]);
}

4.3.5 实现具体操作功能

     在 operations.cpp 中,定义从文件加载数据、添加、显示、修改等操作对应的函数。

  1. (1)从文件加载数据

loadDataFromFile():逐行读取文件中数据,按空格将每个词赋值给Person中的每个成员,得到一个Person对象,并把它保存到数组中。

由于数组长度是固定的,这里应该用可变长的容器对象vector<Person>来保存;为了能够在函数中修改vector,应该把它的引用作为函数参数传入。

  1. (2)添加功能

        首先需要读取键盘输入的金额和备注信息;然后将其保存在一个AccountItem 对象中,并添加到数组,同时写入文件。

person_add():添加联系人主体函数

person_addOffice():记录办公类联系人,将数据添加到vector中,并调用insertIntoFile()函数,写入文件做持久化保存

person_addmyself():记录个人类联系人,将数据添加到vector中,并调用insertIntoFile()函数,写入文件做持久化保存

insertIntoFile():将一条账目写入文件中。

  1. (3)显示功能

        需要用for循环遍历整个数组,通过条件 筛选需要的联系人打印输出,并统计总和。

person_show():查询主体函数

showPerson():显示通讯录联系人

  1. (4)修改功能

        需要用for循环遍历整个数组,通过条件 筛选需要的联系人进行更改,更改后使用for循环再次遍历整个数组,重新写入文件中做持久化保存。

person_revise():修改联系人主体函数

change():遍历数组找到联系人进行更改,将更改后的结果重新写入文件

具体代码如下:

//涉及通讯录操作的函数
#include "common.h"
#include "person_item.h"

//读取文件中的账目数据
void loadDataFromFile(vector<Person>& ps) {
	ifstream input(FILENAME);

	//逐行读取每一条数据,包装成Person对象
	Person p;
	string line;
	while (input >> p.PersonItem >> p.name >> p.iphone >> p.address) {
		ps.push_back(p);
	}
	input.close();
}

//----------------------------------------添加-----------------------------------------------
//添加操作
void person_add(vector<Person>& ps) {
	//读取键盘选择,并做合法化校验
	char key = readMenuSelection(3);
	switch (key)
	{
	case '1':			//办公
		person_addOffice(ps);
		break;
	case '2':			//个人
		person_addmyself(ps);
		break;
	case '3':			//返回主菜单
		break;
	default:
		break;
	}
}

//记录办公类别通讯录
void person_addOffice(vector<Person>& ps) {
	//新构建一个Person对象
	Person p;

	//类型已经确定,就是办公
	p.PersonItem = OFFICE;

	//与用户交互,键盘输入姓名,电话,住址
	cout << "\n请输入姓名:";
	getline(cin, p.name);
	cout << "\n请输入电话:";
	getline(cin, p.iphone);
	cout << "\n请输入住址:";
	getline(cin, p.address);

	//添加到vector中
	ps.push_back(p);
	//写入文件做持久化保存
	insertIntoFile(p);
	//显示成功信息
	cout << "\n----------------添加成功----------------\n";
	cout << "\n请按回车键返回主菜单......." << endl;

	string str;
	getline(cin, str);

}	

//记录个人类别通讯录
void person_addmyself(vector<Person>& ps) {
	//新构建一个Person对象
	Person p;

	//类型已经确定,就是办公
	p.PersonItem = MYSELF;

	//与用户交互,键盘输入姓名,电话,住址
	cout << "\n请输入姓名:";
	getline(cin, p.name);
	cout << "\n请输入电话:";
	getline(cin, p.iphone);
	cout << "\n请输入住址:";
	getline(cin, p.address);

	//添加到vector中
	ps.push_back(p);
	//写入文件做持久化保存
	insertIntoFile(p);
	//显示成功信息
	cout << "\n----------------添加成功----------------\n";
	cout << "\n请按回车键返回主菜单......." << endl;

	string str;
	getline(cin, str);
}


//将一条数据写入文件中
void insertIntoFile(const Person& p) {
	//创建一个ofstream对象,第二个参数表示以追加的方式进行写入
	ofstream output(FILENAME, ios::out | ios::app);

	output << p.PersonItem << "\t" << p.name << "\t" << p.iphone << "\t" << p.address << endl;
	output.close();
}


//-------------------------------------显示----------------------------------------------------------------
void person_show(vector<Person>& ps) {
	//读取键盘选择,并做合法性校验
	char key = readMenuSelection(4);
	switch (key)
	{
	case '1':					//所有
		showPerson(ps);
		break;
	case '2':					//办公
		showPerson(ps,OFFICE);
		break;
	case '3':					//个人
		showPerson(ps,MYSELF);
		break;
	case '4':					//返回主菜单
		break;
	default:
		break;
	}
}

//显示所有通讯录
void showPerson(const vector<Person>& ps) {
	cout << "----------------------查询结果-----------------------" << endl;
	cout << "\n类型\t\t姓名\t\t电话\t\t住址\n" << endl;

	//遍历所有通讯录,并添加数目
	int count = 0;
	for (auto p : ps) {
		printPerson(p);
		++count;
	}
	//输出信息
	cout << "===========================================================" << endl;
	cout << "总联系人数:" << count << endl;
	cout << "\n请按回车键返回主菜单......" << endl;

	string str;
	getline(cin, str);
}

//函数重载,显示联系人
void showPerson(const vector<Person>& ps,const string PersonItem) {
	cout << "----------------------查询结果-----------------------" << endl;
	cout << "\n类型\t\t姓名\t\t电话\t\t\t住址\n" << endl;

	//遍历所有通讯录,并添加数目
	int count = 0;
	for (auto p : ps) {
		if (p.PersonItem != PersonItem)
			continue;
		printPerson(p);
		++count;
	}
	//输出信息
	cout << "===========================================================" << endl;
	cout << "总联系人数:" << count << endl;
	cout << "\n请按回车键返回主菜单......" << endl;

	string str;
	getline(cin, str);
}

//打印每一条通讯录数据
void printPerson(const Person& p) {
	cout << p.PersonItem << "\t\t" << p.name << "\t\t" << p.iphone << "\t\t" << p.address << endl;
}


//----------------------------------------修改---------------------------------------------------------------------------
//修改联系人主体函数
void person_revise(vector<Person>& ps) {
	//读取键盘选择,并做合法化校验
	char key = readMenuSelection(3);

	switch (key)
	{
	case '1':					//办公
		change(ps,OFFICE);

		break;
	case '2':					//个人
		change(ps,MYSELF);

		break;
	case '3':					//退出
		break;					
	default:
		break;
	}
}

//修改联系人操作函数
void change(vector<Person>& ps, string item) {
	cout << "请输入您要修改的联系人姓名:";
	string name;
	bool found = false;
	while (!found) {
		getline(cin, name);
		for (auto& p : ps) {
			if (p.PersonItem == item && p.name == name) {
				string changeNmae;
				string changeIphone;
				string changeAddress;
				cout << "请输入更改后的姓名:";
				getline(cin, changeNmae);
				p.name = changeNmae;
				cout << endl;
				cout << "请输入更改后的电话:";
				getline(cin, changeIphone);
				p.iphone = changeIphone;
				cout << endl;
				cout << "请输入更改后的住址:";
				getline(cin, changeAddress);
				p.address = changeAddress;
				cout << endl;
				found = true;
				break;
			}
		}
		if (!found) {
			cout << "未找到此联系人,请重新输入:" << endl;
		}
	}

	// 将更改写回到文件,	trunc模式会将文件中的数据清空,然后重新写入新的内容
	ofstream outputFile(FILENAME, ios::out | ios::trunc);
	if (outputFile.is_open()) {
		for (const auto& p : ps) {
			outputFile << p.PersonItem << "\t" << p.name << "\t" << p.iphone << "\t" << p.address << endl;
		}
		outputFile.close();
	}
	else {
		cerr << "无法打开文件进行写入." << endl;
	}

	// 显示成功信息
	cout << "----------------修改成功----------------" << endl;
	cout << "请按回车键返回主菜单......." << endl;
	string str;
	getline(cin, str);
}



至此,我们就完成了对通讯录管理的设计与实现啦,你也快去试一下吧~

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

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

相关文章

V.PS荷兰阿姆斯特丹VPS详细测评

V.PS怎么样&#xff1f; V.PS的荷兰VPS位于荷兰阿姆斯特丹数据中心&#xff0c;实际的网络从测评的数据来看&#xff1a;电信走的CN2 GIA/AS4809网络、联通走的是CUII/AS9929网络、移动走的是CUII/AS9929网络&#xff0c;也就是说三网都是走的运营商的轻负载线路。 默认的CPU型…

c/c++自增运算符

自增运算符在前&#xff1a;先自增再取值 自增运算符在后&#xff1a;先取值再自增 如图&#xff1a; lptmp等于tmp&#xff0c;但是t等于128&#xff0c;也就说&#xff0c;当位于后面时&#xff0c;先取值&#xff0c;再自增。

数论第四节:二元一次不定方程、勾股数

不定方程定义 解不确定的方程称为不定方程。一般化的定义为&#xff1a;不定方程是指未知数的个数多余方程的个数&#xff0c;或未知数受到某种限制&#xff08;如整数、正整数等&#xff09;的方程和方程组。 二元一次不定方程定义 形如axbyc的形式的方程。其中a,b不等于0&…

python print 函数参数:sep 自定义分隔符,end 自定义结尾符

1. 简述 print 函数可以将内容打印到标准输出&#xff0c;如果不指定 end 参数&#xff0c;默认在输出的内容之后加一个 “回车符\n”。 以下是 print 函数常用的参数用法&#xff1a; print(object, …, sepstr, endstr) object, …&#xff1a;要打印的内容&#xff0c;可以…

如何基于欧拉系统完成第三方软件仓库的安装

首先&#xff0c;我们需要写一个镜像脚本 rootlocalhost yum.repos.d]# vim docker-ce.repo内容如下 [docker-ce] namedocker baseurlhttps://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/rhel/9/x86_64/stable/ //我们使用的是清华的镜像源 gpgcheck0 tips:这里告诉大家一…

来点八股文(五) 分布式和一致性

Raft raft 会进入脑裂状态吗&#xff1f;描述下场景&#xff0c;怎么解决&#xff1f; 不会。raft通过选举安全性解决了这个问题&#xff1a; 一个任期内&#xff0c;follower 只会投票一次票&#xff0c;且先来先得&#xff1b;Candidate 存储的日志至少要和 follower 一样新…

Kafka基本讲解

Kafka基本讲解 一&#xff1a;Kafka介绍 Kafka是分布式消息队列&#xff0c;主要设计用于高吞吐量的数据处理和消息传输&#xff0c;适用于日志处理、实时数据管道等场景。Kafka作为实时数仓架构的核心组件&#xff0c;用于收集、缓存和分发实时数据流&#xff0c;支持复杂的…

【单例设计模式】揭秘单例模式:从原理到实战的全方位解析(开发者必读)

文章目录 深入理解单例设计模式&#xff1a;原理、实现与最佳实践引言第一部分&#xff1a;设计模式简介第二部分&#xff1a;单例模式定义第三部分&#xff1a;单例模式的优点和缺点第四部分&#xff1a;单例模式的实现方式懒汉式非线程安全的实现线程安全的实现&#xff08;双…

vmware ubuntu虚拟机网络联网配置

介绍vmware虚拟机配置基础网络环境&#xff0c;同时连接外网&#xff08;通过桥接模式&#xff09;&#xff0c;以及ubuntu下输入法等基础工具安装。 本文基于ubuntu22.04&#xff0c;前提虚拟机已经完成安装。本文更多是针对vmware虚拟机的设置&#xff0c;之前有一篇针对ubun…

第三关:Git 基础知识

一、Git是什么 Git是一种开源的分布式版本控制系统&#xff0c;广泛应用于软件开发领域&#xff0c;尤其是在协同工作环境中。它为程序员提供了一套必备的工具&#xff0c;使得团队成员能够有效地管理和跟踪代码的历史变更。下面是 Git 的主要功能和作用的规范描述&#xff1a…

Java面试题——第二篇(设计模式)

1. 工厂方法模式 1.1 普通工厂模式 建立一个工厂类&#xff0c;对实现了同一接口的一些类进行实例的创建。 1.2 抽象工厂模式 抽象多个工厂类&#xff0c;提高工厂的可扩展性 定义抽象工厂接口 public interface DeviceFactory { Phone createPhone(); Computer creat…

【工具插件类教学】vHierarchy 2工具编辑器扩展使用

目录 一、下载导入 二、使用介绍 1.便捷小工具 a.图标和颜色Icons and colors b.对象组件缩略图Component minimap c.层级线展示Hierarchy lines d.极简模式Minimal mode e.斑马条纹图案Zebra striping f.激活切换Activation toggle 2、快捷键 一、下载导入 资源官方…

Redis系列之Redis Sentinel

概述 Redis主从集群&#xff0c;一主多从模式&#xff0c;包括一个Master节点和多个Slave节点。Master负责数据的读写&#xff0c;Slave节点负责数据的查询。Master上收到的数据变更&#xff0c;会同步到Slave节点上实现数据的同步。通过这种架构实现可以Redis的读写分离&…

U盘文件或目录损坏无法读取?专业恢复策略全解析

U盘困境&#xff1a;文件目录的隐形危机 在日常的数字生活中&#xff0c;U盘作为便捷的数据存储与传输工具&#xff0c;扮演着至关重要的角色。然而&#xff0c;当U盘中的文件或目录突然遭遇损坏&#xff0c;导致无法被正常读取时&#xff0c;这无疑给用户带来了极大的困扰。这…

达梦数据库的系统视图v$cachers

达梦数据库的系统视图v$cachers 达梦数据库的系统视图V$CACHERS的作用是显示缓存中的项信息&#xff0c;在 ini 参数 USE_PLN_POOL !0 时才统计。这个视图帮助数据库管理员监控和分析缓存的使用情况&#xff0c;优化数据库性能。通过查询V$CACHERS视图&#xff0c;可以获取缓存…

DeferredResult 是如何实现异步处理请求的

最近遇到了一个问题&#xff0c;我们的一个接口需要去轮询另一个第三方接口&#xff0c;导致这个接口占用了太多工作线程&#xff0c;这些工作线程长时间 running&#xff0c;我们需要解决这个问题。 于是&#xff0c;我们的方案是&#xff1a;用 DeferredResult 实现接口异步。…

如何用 ChatGPT 提升学术写作:15 个高效提示

在本文&#xff0c;我们详细探讨了如何利用 ChatGPT 提升学术写作的各个方面。我们帮助学术作者通过生成创意点子、构建论证结构、克服写作障碍以及格式化引用&#xff0c;从而显著提升其学术论文的质量。这 15 条提示不仅可以单独使用&#xff0c;还可作为学习的良好范例。 本…

文件解析漏洞复现

一、IIS 6.X 1.在网站目录创建文件夹名为xxx.asp/xxx.asa 文件夹&#xff0c;里面的任意文件都会被当作asp文件执行 创建1.asp 访问 2.ooo.asp.jpg会被当做asp文件执行 创建一个ooo.asp;.jpg 访问 二、IIS 7.X 上传1.jpg文件在网址后/.php可以成功执行 写一个1.jpg文件内容…

CTFHubret2shellcode-入土为安的第十三天

checksec pwn 没有开保护 并且是64位的 放入IDa64 shiftf12 查看字符串 发现没有shell f5查看主函数 read函数&#xff0c;点进去buf 0x010,0x08. buf的地址 因为我们要把buf填充满 然后再让栈进入shellcode的地址 然后再执行shellcode recvuntil 函数会一直读取数据&#…

Vue3 + JS项目配置ESLint Pretter

前言 如果在开发大型项目 同时为多人协作开发 那么 ESLint 在项目中极为重要 在使用 ESLint 的同时 也需要使用 Pretter插件 统一对代码进行格式化 二者相辅相成 缺一不可 1. 安装 VsCode 插件 在 VsCode 插件市场搜索安装 ESLint 和 Pretter 2. 安装依赖 这里直接在 pac…