类与对象(上篇)

前言

在之前我们学的C++入门主要是为现在学习类与对象打基础,今天我们才算真正开始学习C++了。因为类与对象的知识点比较多,所以我们将它分为三部分讲解,今天我们学习类与对象的上篇。

一、面向过程和面向对象的初步认识

1、面向过程

面向过程顾名知义,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。例如:C语言。

2、面向对象

面向对象,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。例如:C++,java(注意:C++兼容C,所以C++并不是纯面向对象的语言,是支持面向对象和面向过程的“混编”)。

3、举个例子

外卖系统分别用面向过程与面向对象是怎样实现的?如下图所示:

在这里插入图片描述
tip: 现在大家先对概念理解一下,随着以后的学习会慢慢理解面向对象的思想。

二、类的引入

在C语言中,数据与方法是分离的。而在C++中,数据与方法没有分离。所以在C++中struct被升级成了类——结构体内不仅可以定义变量,也可以定义函数。

代码示例:写一个简易的栈类

#include<iostream>
#include<stdlib.h>

//展开命名空间
using namespace std;
//栈类
typedef int DataType;//栈的元素类型
struct Stack
{
	//成员函数
	//栈的初始化
	void Init(int capacity = 4)
	{
		//在堆区开辟capacity个栈空间
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
			perror("malloc fail");
			return;
		}

		//初始化栈的容量与栈顶位置
		_capacity = capacity;
		_top = 0;//指向栈顶位置的下一个
	}
	//入栈
	void push(const DataType& x)
	{
		//判断是否扩容
		if (_top == _capacity)
		{
			//扩容
		}
		//入栈
		_array[_top] = x;
		_top++;
	}
	//访问栈顶元素
	DataType Top()
	{
		return _array[_top - 1];
	}
	//销毁
	void Destroy()
	{
		free(_array);
		_array = nullptr;
		_capacity = 0;
		_top = 0;
	}

	//成员变量
	DataType* _array;//指向堆区开辟的数组
	int _capacity;//栈的容量
	int _top;//栈顶位置
};

int main()
{
	//C++兼容C,struct以前的用法都可以继续使用
	struct Stack s1;
	//C++类名就是类型,所以可以直接写Stack
	Stack s2;
	s2.Init();//缺省参数——没有传实参,使用缺省值
	s2.push(1);
	s2.push(1);
	cout << s2.Top() << endl;
	s2.Destroy();
	return 0;
}

tip:

①C++将struct升级成了类,不仅可以定义变量,还可以定义函数。

②C++中类名就是类型,在C里面struct 类名组合在一起才是类型。因为C++兼容C所以两种写法都可以,struct以前的用法都可以继续使用。

③我们发现在类中变量成员在声明前面可以使用,这是因为类域是一个整体,所以变量写在后面,也不用声明。

虽然struct被升级成了类,但是在C++中更喜欢用class来代替。

三、类的定义

1、类定义的代码示例

class className
{
	//类体:由由成员变量和成员函数组成
}; //注意与struct一样后面要有分号

解读:

①class是定义类的关键字,className为类名,{}中为类的主体,注意类定义结束时后面分号不能省略。

②类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或成员函数。

2、类的两种定义方法

①声明和定义全部放在类体中 ,需要注意:成员函数如果在类中定义,默认为内联函数,最后由编译器决定。

class Person
{
	//函数的声明与实现都在类中
	void showInfo()
	{
		cout << _name << " " << _age << endl;
	}
	//成员函数
	char* _name;
	int _age;
};

②类声明放在.h文件中,成员函数定义放在.cpp文件中。如下图:

在这里插入图片描述
tip:类成员函数定义时,注意要成员函数名前面要类名::,表明它是那个类的。

总结:对于这两种方法,平时我博客讲解的时候为了方便使用方法1定义类,但是建议大家在以后写项目和工作时使用方法2。

3、成员变量的命名习惯

为了避免成员变量与成员函数的参数同名,我们一般可以①成员变量加前缀_;②成员变量加前缀my_;③成员变量加后缀_等方法。

代码示例:

class Date
{
public:
	void Init(int year)
	{
		//因为成员变量加了前缀_,所以这里我们能很好的区分该语句是给对象的_year赋值
		_year = year;
	}
private:
	int _year;
};

四、类的访问限定符与封装

1、类的访问限定符

(1)引入访问限定符

我们先来看两段代码:

代码1:能运行吗?

struct Person
{
	char _name[10];
	int _age;
};

int main()
{
	//定义对象p1
	Person p1;
	//直接修改对象p1的年龄
	p1._age = 18;
	return 0;
}

代码2:能运行吗?

class Person
{
	char _name[10];
	int _age;
};

int main()
{
	//定义对象p1
	Person p1;
	//直接修改对象p1的年龄
	p1._age = 18;
	return 0;
}

答案是:代码1能运行,代码2不能运行,出现语法错误,这是为什么呢?这就是我们接下来要讲解的类的访问限定符。

(2)访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性的将接口提供给外部的用户使用。

在这里插入图片描述

(3)访问限定符说明

①public修饰的成员在类外可以直接被访问。

②protected和private修饰的成员在类外不能直接被访问。(在C++初阶protected和private类似,在后面进阶讲继承的时候才能体现他们的区别)

③访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。

④如果后面没有访问限定符,作用域就到“}”即类的结束。

⑤class的默认访问权限为private,struct为public(因为struct要兼容C)。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。

(4)问题:C++中struct和class的区别

C++需要兼容C,所以C++中struct可以当作结构体使用。另外C++中struct还可以用来定义类。和class定义类一样,区别是struct定义的类默认访问权限是public,class定义的类默认权限是private。注意:在继承和模板参数列表位置,struct和class也有区别,后序再给大家讲解。

2、封装

(1)面向对象的三大特性

面向对象的三大特性:封装、继承、多态。

(2)什么是封装

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交换。

在我们生活中有许多封装的实例,例如:你家的房子,就是一个封装。如果不封装的话,那谁都可以进你家了。

(3)封装的本质

封装的本质是一种管理,让用户更方便使用类。

在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制那些方法可以在类外直接被使用。

在这里插入图片描述

五、类的作用域

1、类域

①类定义了一个新的作用域,类的所有成员都在类的作用域中。

②在类体外定义成员时,需要使用::作用域操作符指明成员属于哪个类域。

③类域是一个整体,成员变量不是一定写在成员函数后面的。

代码示例:

#include<iostream>
using namespace std;

class Person
{
public:
	void PrintPersonInfo();
private:
	char _name[20];
	char _gender[3];
	int _age;
};

//这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
	cout << _name << " " << _gender << " " << _age << endl;
}

2、简单总结我们已经学过的域

①我们已经学习了四种域:局部域、全局域、命名空间域、类域。

②同一个域不能定义同名变量,不同域可以定义同名变量。

③域都会影响访问,但只有局部域和全局域影响生命周期。

④编译器访问变量规则:一般默认先在局部找,找不到再去全局找(都找不到则报错);特殊类方法先局部找,找不到去类域找,最后再去全局找。

⑤命名空间域与全局域平行,但是如果不展开就不会访问。

⑥::作用域操作符,指定访问某个域的变量。指定方式:域名::变量名。

六、类的实例化

1、定义

用类类型创建对象的过程,称为类的实例化。

2、实例化的说明

①类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。

②一个类可以实例化出多个对象,实例化出的对象才占用实际的物理空间,存储类的成员变量。

类就像是设计图,类实例化出对象就像现实中使用建筑设计图建造出的房子。 图纸并没有实体,同样类也只是一个设计,只有实例化出的对象才能实际存储数据,占用物理空间。

在这里插入图片描述
代码示例:

#include<iostream>
using namespace std;

class Person
{
public:
	void PrintPersonInfo()
	{
		cout << _name << " " << _sex << " " << _age << endl;
	}
public:
	char* _name;
	char* _sex;
	int _age;
};

int main()
{
	//下面语句是否正确?
	//Person::_age = 18;//错误,因为类没有实例化,并没有开辟空间。只有类实例化出的对象才有具体的年龄。
						//tip:类的对象要整体实例化才可以。
	
	//类实例化对象/对象定义
	Person man;
	//tip:只有类实例化,开辟了空间,才能存储数据
	man._name = (char*)"zhangsan";
	man._sex = (char*)"男";
	man._age = 18;
	man.PrintPersonInfo();
	return 0;
}

七、类对象模型

1、如何计算类的大小

问题: 类中既有成员变量,也有成员函数,那么一个类的对象中包含什么?如何计算一个类的大小?

我们先来一段代码示例,用编译器运行计算看类A的大小是多少。

#include<iostream>
using namespace std;

class A
{
public:
	//成员函数
	void f()
	{}
private:
	//成员变量
	int _a;
	char _ch;
};

int main()
{
	//实例化对象
	A a;
	//打印类对象的大小
	cout << sizeof(a) << endl;
	return 0;
}

运算结果:类A的大小

在这里插入图片描述
解读:

①类对象存储: 类对象只保存类的成员变量,不保存类的成员函数。(为什么会这样呢?详细讲解在后面类对象存储猜测)

②类的大小计算: 与C语言计算结构体的方式一样,需要注意内存对齐。

③回顾内存对齐的规则:

1、结构体的第一个成员,对齐到结构体变量在内存中存放位置的0偏移量。

2、从第二个成员开始,每个成员变量都要对齐到(一个对齐数)的整数倍。

  • 对齐数=编译器默认的一个对齐数与一个结构体成员自身大小的较小值。
  • VS默认对齐数为8;Linux gcc没有默认对齐数,对齐数就是结构体成员自身大小。


3、结构体总大小,必须是所有成员变量的对齐数中最大对齐数的整数倍。

4、如果是嵌套结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

④内存对齐的意义:

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定的数据,否则抛出硬件异常(例:int——>对齐到4的整数倍)。

2、性能原因:计算机读取数据时,并不是想访问哪个字节就访问哪个字节,而是从结构体的对齐边界开始按照访问倍数去访问。假设CPU一次访问4个字节(具体与硬件有关),对齐访问_a只要访问一次,不对齐_a要访问两次,如下图所示:

在这里插入图片描述
如图可知,内存对齐是拿空间换时间。

⑤类A的内存大小为8,如图所示:

在这里插入图片描述

2、类对象的存储猜测

①对象中包含类的各个成员:

在这里插入图片描述
缺陷: 不同对象中成员变量不同,但是调用同一个函数,如果按照此方式存储,当一个类创建多个对象时,**每个对象都会保存一份代码,相同代码保存多次,浪费空间。

②只保存成员变量,成员函数存放在公共的代码段:

在这里插入图片描述
tip:关于上述两种存储方式,计算机按照方式二来存储。

3、空类与仅有成员函数的类大小

代码示例:

#include<iostream>
using namespace std;

//类中仅有成员函数
class A1
{
public:
	void f()
	{}
};

//类中什么都没有——空类
class A2
{};

int main()
{
	//输出A1,A2的大小
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
	return 0;
}

运行结果:

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

①没有成员变量的类对象,需要一个字节,是为了占位(不存储有效数据),表示对象存在。

②一个类的大小,实际就是该类中成员变量之和,还需要注意内存对齐。

八、this指针

1、this指针的引出

我们来定义一个日期类Data:

#include<iostream>
using namespace std;

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year; 
	int _month;
	int _day; 
};
int main()
{
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	d2.Print();
	return 0;
}

运行结果:

在这里插入图片描述

问题: d1与d2调用同一个函数print,为什么打印结果不一样呢?(在类的存储模型我们知道成员函数与对象无关,它存储在公共区域。)

答案是: C++中引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数”添加了一个隐藏的this指针参数,让该this指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。如下图所示:

在这里插入图片描述

2、this指针的特点

(1)语法规定:this指针不能在形参和实参显示传递,但是可以在函数内部显示使用

①调用成员函数时,不能显示传递对象的地址给this,因为编译器会自动传递了,不需要用户传递。

tip: 既然编译器会自动传递对象地址,为什么不通过类来调用函数——因为通过类来调用函数,不会传this指针;通过对象调用函数,虽然不再对象里找函数,但是会传this指针。

②定义成员函数时,也不能显示定义this指针,因为编译器会自动定义。

tip: this指针是一个关键字,指向当前对象地址。

③this指针可以在函数体内部显示使用,如下代码所示:

void Print()
	{
		//语法规定:this指针不能在形参和实参显示传递,但是可以在函数内部显示使用
		//函数体中所有成员变量都要通过this指针访问
		//1、在函数体中,访问成员变量,你不写他会自动添加this
		cout << _year << "-" << _month << "-" << _day << endl;
		//2、在函数体内可以自己显示使用this指针
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}

tip: 在函数内部,你访问对象的成员变量你不写this指针它会自动添加,你也可以显示使用。

(2)this指针的类型:类类型* const,即成员函数中,不能给this指针赋值

tip:左定值,右定向。

const在*的左边,则指针指向的变量的值不能通过指针改变;在 * 的右边,则指针的指向不能改变。

代码示例:

int main()
{
	int a = 10;
	//右定向
	int* const pa = &a;
	int b = 9;
	//pa = &b;//报错,左值不可修改,即指针指向不能改变
	*pa = b;//可以修改指向变量的值
	return 0;
}

3、面试题

问题1: this指针存在哪里?

答案是:this指针本质上是“成员函数”的形参,所以this指针与普通参数一样存在函数调用的栈帧里面。如下图汇编代码:

在这里插入图片描述
tip: 在VS集成开发环境下,对this指针进行了优化,对象地址是放在ecx寄存器,ecx存储this指针的值。

问题2: this指针可以为空吗?

我们先来看两段代码,判断A、编译报错 B、运行崩溃 C、正常运行

代码1:

class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

代码2:

class A
{
public:
	void PrintA()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->PrintA();
	return 0;
}

答案是: 代码1:C、运行成功;代码2:B、运行崩溃。解析如下图所示:

在这里插入图片描述

4、this指针的好处

在这里插入图片描述

tip: C就像手动挡需要自己控制变速箱,C++有了封装,引入this指针就像自动挡电脑程序控制变速箱。简单来说,就是更简单,不易出错了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于安卓android微信小程序的个人管理小程序

运行环境 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&a…

Java架构师软件架构设计导论

目录 1 软件架构设计导论2 HR角度看架构师3 软件架构设计概述4 顶级大师眼中的架构5 建筑中的架构师6 软件架构的发展阶段7 软件架构的意义8 架构是项目干系人进行交流的手段9 架构有助于循序渐进的原型设计10 架构是设计决策的体现11 架构明确系统设计约束条件12 架构与组织结…

【python】直方图正则化详解和示例

直方图正则化&#xff08;Histogram Normalization&#xff09;是一种图像增强技术&#xff0c;目的是改变图像的直方图以改善图像的质量。具体来说&#xff0c;它通过将图像的直方图调整为指定的形状&#xff0c;以增强图像的对比度和亮度。 直方图正则化的基本步骤如下&…

[计算机网络]网络层概述

呼,写了这么久终于重新开始啦! 自己落下了太多东西了.....是时候应该重新拾掇起来了. 关于后面的代码项目,我的想法是vilas.js仍然使用js来进行编写,但是后续其他的项目会开始尝试使用ts来进行书写了. 就算是前端也需要点规范吧..... 0.写在前面 这篇文章要和大家道个歉,首…

“非旺玖原装的PL2303,.........“解决办法

"非旺玖原装的PL2303&#xff0c;…"解决办法 windows安装PL2303串口驱动提示“非旺玖原装的PL2303&#xff0c;请联系您的供货商”的解决办法&#xff1a; 主要原因&#xff1a;驱动版本无法兼容&#xff0c;需要降低使用版本 解决办法&#xff1a; 1.插好串口线&…

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志分层 前言 在 Python 的logging模块中&#xff0c;它不仅提供了基础的日志功能&#xff0c;还拥有一系列高级配置选项来满足复杂应用的日志管理需求。 说到logging 模块的高级配置&#xff0c;必须提及日志分…

HDCTF2023 - Reverse方向全WP

文章目录 [HDCTF 2023]easy_re[HDCTF 2023]easy_asm[HDCTF 2023]fake_game[HDCTF 2023]enc[HDCTF 2023]double_code[HDCTF 2023]买了些什么呢[HDCTF2023]basketball [HDCTF 2023]easy_re UPX壳&#xff0c;脱壳 一个base64编码。 [HDCTF 2023]easy_asm ida打开后可以看到xor 10…

Ubuntu18.04运行gazebo的launch文件[model-4] process has died报错

启动gazebo仿真环境报错[model-4] process has died [model-4] process has died [pid 2059, exit code 1, cmd /opt/ros/melodic/lib/gazebo_ros/spawn_model -urdf -model mycar -param robot_description __name:model __log:/root/.ros/log/8842dc14-877c-11ee-a9d9-0242a…

opencv将32位深图片合成视频跳帧解决办法

在合成视频时候&#xff0c;大多数的图片都是24位深度的&#xff08;即RGB三通道&#xff0c;一个通道8位&#xff09;&#xff0c;但是也存在少量的32位深的图片&#xff08;RGBA&#xff0c;三个颜色通道加上A这个透明度通道&#xff09;&#xff0c;32位和24位的格式是不一样…

SSM2

DataSource mybatis与Spring整合 事务加载业务层上面 开启事务驱动 上面都是声明式开启事务 图书管理系统 命名规范: java命名规范:驼峰命名法类:大驼峰变量,属性名.方法名:小驼峰 常量使用下划线分割:全大写,单词与单词之间下划线分割数据库命名规范:常用命名规范:下划线…

【Windows 常用工具系列 11 -- 福昕PDF搜索高亮过的文本】

文章目录 福昕 PDF 搜索高亮过的文本 福昕 PDF 搜索高亮过的文本 在 pdf 文档阅读过程中&#xff0c;我们需要经常高亮一些文本&#xff0c;以方便下次阅读时找到重点。我这边使用的是 福昕PDF 阅读器&#xff0c;下面就介绍下如何在福昕阅读器中搜索已经高亮过的文本。

基于单片机PM2.5监测系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机PM2.5监测系统仿真设计 文章目录 一 概要简介设计目标系统组成工作流程仿真设计结论 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于单片机PM2.5监测系统仿真设计介绍 简介 PM2.5&#xff08;可吸…

Django实战:从零到一构建安全高效的Web应用

目录 一、概述 二、版本控制和部署 1、Git版本控制 2、Docker部署 三、数据库配置 1、配置数据库设置 2、创建数据库模型 四、URL路由和视图 1、定义URL路由 2、创建视图 五、模板渲染 1、创建模板 2、在视图中使用模板 总结 一、概述 Django是一个高级Python W…

【机器学习】038_梯度消失、梯度爆炸

一、原因 神经网络梯度 假设现在有一个 层的神经网络&#xff0c;每层的输出为一个对输入作 变换的函数结果 用 来表示第 层的输出&#xff0c;那么有下列公式&#xff1a; 链式法则计算损失 关于某一层某个参数 的梯度&#xff1a; 注意到&#xff0c; 为向量&am…

【NI-RIO入门】CompactRIO介绍及环境安装

CompactRIO是什么&#xff1f; CompactRIO系统提供了高处理性能、传感器专用I/O和紧密集成的软件工具&#xff0c;使其成为工业物联网、监测和控制应用的理想之选。实时处理器提供可靠&#xff0c;可预测的行为&#xff0c;而FPGA在需要高速逻辑和精确定时的较小任务上表现出色…

11.20 知识总结(choices参数、MVC和MTV的模式、Django与Ajax技术)

一、 choices参数的使用 1.1 作用 针对某个可以列举完全的可能性字段&#xff0c;我们应该如何存储 .只要某个字段的可能性是可以列举完全的&#xff0c;那么一般情况下都会采用choices参数 1.2 应用场景 应用场景&#xff1a; 学历&#xff1a; 小学 初中 高中 本科 硕士…

MATLAB | 官方举办的动图绘制大赛 | 第二周赛情回顾

今天带来一下MATHWORKS官方举办的迷你黑客大赛第三期(MATLAB Flipbook Mini Hack)的最新进展&#xff01;&#xff01;目前比赛已经进行了两周非常荣幸能够成为第一周的阶段性获奖者&#xff1a; 本来并不打算每周进行一次赛况讲解&#xff0c;但是由于字符限制改成了2000&…

【MySql】13- 实践篇(十一)

文章目录 1. 自增主键为什么不是连续的&#xff1f;1.1 自增值保存在哪儿&#xff1f;1.2 自增值修改机制1.2.1 自增值的修改时机1.2.2 自增值为什么不能回退? 1.3 自增锁的优化1.3.1 自增锁设计历史 2. Insert语句为何很多锁?2.1 insert … select 语句2.2 insert 循环写入2…

Revive开发商加入VR开源标准OpenXR

导读作为一款能让HTC Vive用户玩到Oculus平台游戏的软件&#xff0c;它的开发商CrossVR今日宣布即将加盟为VR和AR应用程序开源组织&#xff0c;即OpenXR。 由Khronos Group引领的OpenXR旨在创建一个标准化且免版税的应用程序编程接口&#xff08;API&#xff09;&#xff0c;该…

60 权限提升-MYMSORA等SQL数据库提权

目录 数据库应用提权在权限提升中的意义WEB或本地环境如何探针数据库应用数据库提权权限用户密码收集等方法目前数据库提权对应的技术及方法等 演示案例Mysql数据库提权演示-脚本&MSF1.UDF提权知识点: (基于MYSQL调用命令执行函数&#xff09;读取数据库存储或备份文件 (了…