【C++】类和对象①(什么是面向对象 | 类的定义 | 类的访问限定符及封装 | 类的作用域和实例化 | 类对象的存储方式 | this指针)

目录

前言

什么是面向对象?

类的定义

类的访问限定符及封装

访问限定符

封装

类的作用域

类的实例化

类对象的存储方式

this指针

结语


前言

最早的C++版本(C with classes)中,最先加上的就是类的机制,它构成了面向对象编程(OOP)的基础。封装,继承,多态作为面向对象编程(OOP)的三大特性,极大的降低了我们项目过程中的代码编写和维护难度。类使C++有了与C语言不同的编程模式(OOP),这也是让其区别于C语言成为一名独立编程语言的诸多原因之一。本篇博客是C++类和对象内容的第一篇,将会介绍什么是面向对象,关于最基础类的定义,类的访问限定符以及封装等内容。

什么是面向对象?

在来到C++之前,想必大家已经接触过C语言,C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。拿洗衣服来举例,如果采用面向过程的逻辑,那么洗衣服这个过程是这样的:

C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完
成。这次同样拿洗衣服来举例,如果采用面向对象的逻辑,洗衣服这个过程就是这样的:

面向对象(Object-Oriented,简称OO,OOP就是Object Oriented Programming)是一种程序设计模型,它将数据和操作数据的方法绑定在一起,作为一个单独的对象。在面向对象编程中,程序员定义的是对象,这些对象包含数据(属性)以及操作这些数据的方法(函数)。

面向对象和面向过程的主要区别体现在以下几个方面:

  1. 关注点
    • 面向过程(Procedure-Oriented):关注于步骤与流程,即先做什么,后做什么。它按照步骤一步一步解决问题,是一种线性化的思考方式。
    • 面向对象(Object-Oriented):关注于对象,对象由数据和操作数据的方法组成,它将现实世界中的事物抽象为对象,并通过对象之间的交互来解决问题。
  2. 复用性
    • 面向过程:代码复用性差,因为函数是独立的,需要重复编写相似的逻辑。
    • 面向对象:代码复用性好,因为可以通过继承和多态等机制,让子类复用父类的属性和方法。
  3. 扩展性
    • 面向过程:随着程序规模的增大,维护和扩展变得困难。
    • 面向对象:由于数据和方法的封装,使得程序的维护和扩展变得相对容易。
  4. 设计思路
    • 面向过程:是自顶向下的设计,即先考虑整体架构,再逐步细化。
    • 面向对象:是自底向上的设计,即先定义对象,然后让对象之间通过消息传递来交互,形成整个系统。

到这里,大家应该能稍微体会到面向对象逻辑了。

类的定义

上面刚刚提到,面向对象的设计模式能将数据(成员变量)操作数据的方法(函数)绑定在一起,具体是怎么绑定的,这就是我们要引入的内容——

C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量现在以C++方式实现,会发现struct中也可以定义函数

上面结构体的定义,在C++中更喜欢用class来代替。没错,当你使用class来定义成员变量和函数时,你就已经触及到类最基本的用法了。

class className
{
        // 类体:由成员函数和成员变量组成
}; // 注意后面的分号

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

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

类的两种定义方式

1. 声明和定义不分离:声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理(类内部定义的函数默认加了inline,不过是否内联取决于编译器)。

2. 声明和定义分离:类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

一般情况下,更期望采用第二种方式。

注:在博客的案例代码中方便演示一般使用方式一定义类,大家后序工作中尽量使用第二种

 类成员变量的命名规则

class Date
{
public:void Init(int year){// 这里的year到底是成员变量,还是函数形参?year = year;}
private:int year;
};

为了避免上面这种命名造成的麻烦,我们对成员变量一般采取前面加 _(下划线)的命名方式,如下:

class Date
{
public:void Init(int year){_year = year;}
private:int _year;
};

类的访问限定符及封装

访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选
择性的将其接口提供给外部的用户使用
。C++中的类的访问限定符用来控制类成员的访问权限。它们主要有三种:publicprotectedprivate。这些访问限定符决定了类的成员(包括数据成员和成员函数)在类外部的可访问性。

通俗地说,这些访问限定符就像是一道道门,控制谁能进入这个房间(类),谁能看到或操作房间里的哪些东西(类的成员)。

  1. public

    • 公开的门。任何外部的代码都可以直接访问public成员。这就像是房间的门完全打开,任何人都可以自由进出,看到并操作房间里的所有东西。
    • public修饰的成员在类外可以直接被访问。
  2. protected

    • 半掩的门。protected成员在类的外部是不可直接访问的,但在派生类中却是可以访问的。这就像是房间的门半开着,外面的人不能直接进去,但房间里的某些特定的人(派生类)可以进去。
    • 它通常用于那些需要在派生类中继承但不需要在类外部直接访问的成员。
  3. private

    • 锁着的门。private成员只能被类的成员函数和友元函数访问,外部的代码是无法访问的。这就像是房间的门完全锁着,只有持有钥匙的人(类的成员函数和友元函数)才能打开并进入。
    • 它通常用于那些不需要在类外部被直接访问的数据成员,确保数据的封装性和安全性。

对于上面大家没有学过的词目前可以不必深究,这里我们需要知道的内容有:

  1. public修饰的成员在类外可以直接被访问。
  2. protected和private修饰的成员在类外不能直接被访问(在没有学习继承多态前可以认为protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct默认访问权限为public(因为struct要兼容C)

:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

成员变量b和c无法在类外部直接访问,但是在类的内部,比如类的成员函数中是可以随意访问的。

你可能会问C++中的struct和class是否有区别?这也是一道面试题:
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的区别是struct定义的类默认访问权限是publicclass定义的类默认访问权限是private

注:在继承和模板参数列表位置,struct和class也有区别,后序给大家介绍。

封装

这就讲道了面向对象三大特性(封装,继承,多态)之一的封装。

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

封装本质上是一种管理,让用户更方便使用类。封装的主要目的是隐藏对象的属性和实现细节,仅对外公开接口,以达到提高代码安全性、可维护性和可复用性的目的。比如:对于电脑这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。

类的作用域

类定义了一个新的作用域,每个类都具有自己的类域,该类的成员(包括数据成员和成员函数)都局部于该类所属的类域中。这意味着在类的定义中声明的成员名被引入类的作用域中,而两个不同的类具有两个独立的类作用域。即使两个类具有完全相同的成员列表,它们也是不同的类型,每个类的成员不同于任何其他类的成员。类的所有成员都在类的作用域中在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域

class A
{
public:void Print();
};
// 这里需要指定Print是属于A这个类域
void A::Print()
{cout << "hello" << endl;
}
int main()
{A a;a.Print();return 0;
}

类的实例化

类的实例化是指创建一个类的对象的过程。简单来说,类的实例化就是将抽象的类转化为具体的对象的过程。类是对对象进行描述的,这个对象会拥有类中定义的属性和方法,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息。

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

在上面的代码例子当中,A类是没有空间的,只有A实例化出来的对象aa才能访问其数据成员和成员函数。做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。

类对象的存储方式

class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};int main()
{A aa;cout << "sizeof(A):" << sizeof(A) << endl;cout << "sizeof(aa):" << sizeof(aa) << endl;return 0;
}

在我们使用类创建对象的过程中,是否像过对象的内存具体是如何存储的呢?让咱们来探索一下,我们可以给类对象存储方式做出如下三种猜测。

1. 对象中包含类的多个成员

缺陷:此种放方式将成员变量和成员函数统统在创建对象的时候存储一遍,每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢

2. 代码只保存一份,在对象中保存存放代码的地址

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

想要探索具体是以上三种方式的哪一种,可以自己动手通过编译器测试一下。

会发现,PrintA这个函数,并没有在对象中占内存空间。一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐(和C语言中struct的对齐规则相同)。什么?你不知道什么是内存对齐?没关系,我之前也专门写过一篇博文有struct内存对齐相关规则探索,可以进去看看了解一下:自定义类型-结构体,联合体和枚举-C语言-CSDN博客

注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象,如下:

// 类中仅有成员函数
class A2 {
public:void f2() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{cout << "sizeof(A2):" << sizeof(A2) << endl;cout << "sizeof(A3)" << sizeof(A3) << endl;return 0;
}

经过如上验证,第三种存储方式才是C++真正所采用的。

this指针

在C++中,this指针是一个特殊的指针,它存在于类的非静态成员函数中。它指向调用该成员函数的对象本身。当我们在成员函数内部想要引用当前对象时,就可以使用this指针。

概念this指针是类的一个隐含的形参,编译器在调用成员函数时自动传递当前对象的地址。this指针总是指向成员函数所属对象的内存地址。

this指针只存在于类的非静态成员函数中。静态成员函数没有this指针,因为静态成员函数不依赖于特定的对象实例,它们与类本身相关联。

当我们调用一个非静态成员函数时,实际上编译器会自动传递当前对象的地址给该函数。我们不需要显式地传递this指针,它是隐式传递的。

this指针用途

  1. 当我们需要在成员函数中返回对象本身时,可以使用return *this;,这时this就派上了用场。
  2. 在函数内部,如果成员变量和局部变量同名,我们可以使用this->来引用成员变量。
  3. 可以通过this指针在函数内部获取对象的大小,使用sizeof(*this)

下面是this指针的使用案例,大家可以对照规则去感受其使用。

class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}Date& Copy(const Date& d) //此函数为赋值{_year = d._year;_month = d._month;_day = d._day;// 用法1return *this;// 可以实现连续赋值}void Print(){// 用法2cout << this->_year << "-" << this->_month << "-" << this->_day << endl;}void PrintSize(){//用法3cout << sizeof(*this) << endl;}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2, d3;d1.Init(0, 0, 0);d2.Init(0, 0, 0);d3.Init(2022, 1, 12);d1.Print();d2.Print();d3.Print();cout << endl;d1 = d2.Copy(d3);d1.Print();d2.Print();d3.Print();d1.PrintSize();return 0;
}

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

this指针特性

  • this指针的类型:类型* const,即成员函数中,不能给this指针赋值。
  • 只能在“成员函数”的内部使用
  • this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  • this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。

以下有两道面试真题:

1. this指针存储在哪?

在C++中,this指针是一个隐含的指针,它并不显式地存储在对象的内存布局中。相反,this指针是在编译时由编译器自动处理的。当非静态成员函数被调用时,编译器会隐式地将当前对象的地址作为this指针传递给该函数。这个传递是自动的,程序员不需要显式地传递它。

在成员函数内部,this指针是一个指向调用该函数的对象本身的指针。它指向当前对象的内存地址,因此可以用来访问该对象的成员变量和成员函数。

尽管this指针在成员函数内部是可见的,但它并不占用对象本身的内存空间。它更像是一个函数调用的上下文信息,由编译器在内部处理。

需要注意的是,this指针的存在是依赖于非静态成员函数的调用的。静态成员函数没有this指针,因为静态成员函数不依赖于特定的对象实例,而是与类本身相关联。

总结来说,this指针在C++中是一个编译时构造,由编译器自动处理,并不显式地存储在对象的内存布局中。它指向当前对象的内存地址,用于在成员函数内部访问对象的成员

2. this可以为空吗?

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}

大家的第一反应可能是A或者B,那就大错特错了,这个程序能运行,而且运行的很好。

再来看看下面这段代码。

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}

这段代码在运行的时候会崩溃。两段代码能否运行的区别在于,是否对空的this指针做出解引用操作,在第一段代码中,找成员函数和传递this指针的底层不会对空指针(nullptr)进行解引用,故能成功运行;然而第二段代码就不同了,其成员变量_a在访问过程中的底层为(this->_a),实际上对this(nullptr)进行了解引用操作,故程序崩溃。

结语

关于类入门的基本内容到这里就结束了,本篇博客对面向对象编程做了基本的介绍,介绍了类的基本定义方式和使用方法,访问限定符以及面向对象三大特性之一的封装,探讨了类的底层存储方式,实例化方式,最后还讲到了特殊的this指针。C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知,大大方便了编程。

博主后续会产出更多有意思的内容,感谢大家支持!♥

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

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

相关文章

不使用 Docker 构建 Triton 服务器并在 Google Colab 平台上部署 HuggingFace 模型

Build Triton server without docker and deploy HuggingFace models on Google Colab platform EnvironmentBuilding Triton serverDeploying HuggingFace models客户端推荐阅读参考 Environment 根据Triton 环境对应表 &#xff0c;Colab 环境缺少 tensorrt-8.6.1&#xff0…

如何客观评价5G的现状?

前几天&#xff0c;在知乎上看到一个帖子&#xff0c;热度挺高&#xff1a; 看了一下帖子的回答&#xff0c;基本上都在骂5G。 作为通信行业从业者&#xff0c;我说说我自己的看法。大家姑且听听&#xff0c;一起交流一下。 我们目前所处的这个时代&#xff0c;有一个很大的特点…

使用Node.js模拟执行JavaScript

使用Node.js模拟执行JavaScript 模拟执行的是JavaScript&#xff0c;而且依赖的是Node.js&#xff0c;为什么不直接用Node.js来尝试JavaScript的执行呢&#xff1f;其实是完全可行的。 准备工作 确保已经正确安装好了Node.js。安装流程可以在小蜜蜂AI网站获取。 模拟执行 …

Day17_学点JavaEE_转发、重定向、Get、POST、乱码问题总结

1 转发 转发&#xff1a;一般查询了数据之后&#xff0c;转发到一个jsp页面进行展示 req.setAttribute("list", list); req.getRequestDispatcher("student_list.jsp").forward(req, resp);2 重定向 重定向&#xff1a;一般添加、删除、修改之后重定向到…

Flutter如何集成到已有iOS工程上

大家好&#xff0c;我是咕噜铁蛋&#xff0c;今天我将和大家分享一个实用的技术教程——如何将Flutter集成到已有的iOS工程中。Flutter是Google推出的一款开源的移动UI框架&#xff0c;它允许开发者使用Dart语言来开发高性能、美观的原生应用&#xff0c;并支持iOS和Android两大…

Walmart.com DSV XML对接需求

此前的文章Walmart.com DSV EDI对接需求中&#xff0c;为大家介绍了如果选择传输EDI文件需要做的准备与需求。本文将为大家介绍Walmart.com 与DSV&#xff08;Drop Ship Vender&#xff09;之间传输XML文件的需求。与EDI相比&#xff0c;XML文件的处理难度相对低一些。无论企业…

stm32GPO的相关操作

GPIO的使用 1.GPIO八种工作模式1.1 上拉输入1.2 下拉输入1.3 浮空输入1.4 模拟输入1.5 推挽输出1.6 开漏输出1.7 复用推挽输出1.8 复用开漏输出 2.相关寄存器2.1 寄存器配置IO 3.相关库函数 1.GPIO八种工作模式 保护二极管的作用&#xff1a;用来保护IO&#xff0c;一般情况IO的…

【Linux】TCP编程{socket/listen/accept/telnet/connect/send}

文章目录 1.TCP接口1.1socket文档 1.2listen拓&#xff1a;端口号8080 1.3accept拓&#xff1a;今天全局函数 1.4读写接口1.5telnet1.一个客户端2.两个客户端 1.6ulimit -a1.7常识回顾1.8connect1.9拓&#xff1a;客户端的ip和地址什么时候被分配&#xff1f;1.10拓&#xff1a…

设计模式之命令模式讲解

概念&#xff1a;命令模式&#xff08;Command Pattern&#xff09;又称行动&#xff08;Action&#xff09;模式或交易&#xff08;Transaction&#xff09;模式。将一个请求封装成一个对象&#xff0c;从而让你使用不同的请求把客户端参数化&#xff0c;对请求排队或者记录请…

数据结构:冒泡排序,快速排序,插入排序

冒泡排序&#xff0c;每次只排一个&#xff0c;像鱼吐泡泡一样&#xff0c;从数组最后开始两两交换&#xff0c;一次只找到一个当前最小的&#xff0c;放到第一个,第二个...位置. T(n)O(n的平方&#xff09;,有序O(n) S&#xff08;n&#xff09;O&#xff08;1&#xff09; #i…

使用 Spring Boot 和 Maven 引入本地 Jar 包

背景 在 Java 开发中&#xff0c;有时候我们需要引入本地的 Jar 包到项目中&#xff0c;以满足特定的功能需求。本文将以引入 id 生成器为例&#xff0c;介绍如何在 Spring Boot 项目中使用 Maven 管理本地 Jar 包。 准备工作 创建 libs 目录&#xff1a; 在项目根目录下创建…

spring eureka 服务实例实现快速下线快速感知快速刷新配置解析

背景 默认的Spring Eureka服务器&#xff0c;服务提供者和服务调用者配置不够灵敏&#xff0c;总是服务提供者在停掉很久之后&#xff0c;服务调用者很长时间并没有感知到变化。或者是服务已经注册上去了&#xff0c;但是服务调用方很长时间还是调用不到&#xff0c;发现不了这…

pandas,polars,pyspark的df对象常见用法对比

案例背景 最近上班需要处理的都是百万&#xff0c;千万级的数据&#xff0c;pandas的性能已经不够看了&#xff08;虽然它在处理数据上是真的很好用&#xff09;&#xff0c;公司都是用的polar和pyspark&#xff0c;我最近也学习了一些&#xff0c;然后写篇文章对比一下他们的…

达梦使用disql登录数据库显示“未连接”

基础环境 操作系统&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) 数据库版本&#xff1a;DM Database Server 64 V8 架构&#xff1a;单实例问题&#xff1a;达梦数据库在使用disql登录时&#xff0c;显示“未连接”。 指定了IP和端口号还是连接异常。 […

CentOS如何做端口映射?

在今天的技术发展中&#xff0c;越来越多的应用需要跨越网络进行远程管理和控制。为了实现这一目标&#xff0c;端口映射技术被广泛应用于各个领域。其中&#xff0c;【天联】作为一种性能稳定、安全可靠的端口映射工具&#xff0c;在各种应用场景中得到了广泛的应用和认可。 结…

python+django+flask+vue贫困地区儿童资助网站22pk7

Python 中存在众多的 Web 开发框架&#xff1a;Flask、Django、Tornado、Webpy、Web2py、Bottle、Pyramid、Zope2 等。近几年较为流行的&#xff0c;大概也就是 Flask 和 Django 了 一开始&#xff0c;本文就对系统内谈到的基本知识&#xff0c;从整体上进行了描述&#xff0c…

SEO优化艺术:精细化技巧揭示与搜索引擎推广全面战略解读

SEO&#xff08;搜索引擎优化&#xff0c;Search Engine Optimization&#xff09;是一种网络营销策略&#xff0c;旨在通过改进网站内外的各项元素&#xff0c;提升网站在搜索引擎自然搜索结果中的排名&#xff0c;从而吸引更多目标用户访问网站&#xff0c;增加流量&#xff…

面试算法-154-搜索二维矩阵 II

题目 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,…

鸿蒙ArkUI声明式学习:【UI资源管理】

OpenHarmony 应用的资源分类和资源的访问以及应用开发使用的像素单位以及各单位之间相互转换的方法。 资源分类 移动端应用开发常用到的资源比如图片&#xff0c;音视频&#xff0c;字符串等都有固定的存放目录&#xff0c;OpenHarmony 把这些应用的资源文件统一放在 resourc…

python+django教师业绩考评考核评分系统flask

在设计过程中&#xff0c;将参照一下国内外的一些同类网站&#xff0c;借鉴下他们的一些布局框架&#xff0c;将课题要求的基本功能合理地组织起来&#xff0c;形成友好、高效的交互过程。开发的具体步骤为&#xff1a;   第一步&#xff0c;进行系统的可行性分析&#xff0c…