C++——入门基础(上)

目录

一、C++参考文档

二、C++在工作领域的应用

三、C++学习书籍

四、C++的第一个程序

五、命名空间

(1)namespace的定义 

(2)命名空间的使用

六、C++的输入和输出 

七、缺省函数

八、函数重载

九、写在最后 


一、C++参考文档

(1)虽然不是C++官方文档且标准只更新到C++11,但是以头文件的形式呈现,内容易看好懂;

https://legacy.cplusplus.com/reference/

(2)C++官方文档的中文版和英文版,信息很全,更新到了最新的C++标准,但是相比第一个不是那么易看。

https://zh.cppreference.com/w/cpp

https://en.cppreference.com/w/

二、C++在工作领域的应用

(1)大型软件开发。如:编译器、数据库、操作系统、浏览器等。

(2)音视频处理

(3)PC客户端开发。⼀般是开发Windows上的桌面软件,比如WPS之类的;技术栈的话一般是C++和 QT,QT是⼀个跨平台的C++图形用户界面(Graphical User Interface,GUI)程序。

(4)服务端开发。各种大型应⽤网络连接的高并发后台服务。这块Java也比较多,C++主要用于⼀些对性能要求比较高的地方。如:游戏服务、流媒体服务、量化高频交易服务等。

(5)游戏引擎开发。很多游戏引擎就都是使⽤C++开发的,游戏开发要掌握C++基础和数据结构,学习图形学知识,掌握游戏引擎和框架,了解引擎实现,引擎源代码可以学习UE4、Cocos2d-x等开源引擎实现。

(6)嵌入式开发。嵌入式把具有计算能力的主控板嵌入到机器装置或者电子装置的内部,通过软件能够控制这些装置。比如:智能手环、摄像头、扫地机器人、智能音响、门禁系统、车载系统等等,粗略⼀点,嵌入式开发主要分为嵌⼊式应⽤和嵌⼊式驱动开发。

(7)测试开发/测试

(8)机器学习引擎。机器学习引擎。机器学习底层的很多算法都是用C++实现的,上层用python封装起来。如果你只想准备数据训练模型,那么学会Python基本上就够了,如果你想做机器学习系统的开发,那么需要学会C++。

三、C++学习书籍

(1)C++ Primer:主要讲解语法,经典的语法书籍,前后中期都可以看,前期如果自学看可能会有点晦涩难懂,中后期作为语法字典,非常好用。

(2)STL源码剖析:主要从底层实现的角度结合STL源码,庖丁解牛式剖析STL的实现,是侯捷老师的经典之作。可以很好的帮助我们学习别人用语法是如何实现出高效简洁的数据结构和算法代码,如何使用泛型封装等。

(3)Effctive C++:本书也是侯捷老师翻译的,本书有的⼀句评价,把C++程序员分为看过此书的和没看过此书的。本书主要讲了55个如何正确高效使用C++的条款。

四、C++的第一个程序

C++兼容C语言绝大多数的语法,所以C语言实现的hello world依旧可以运行:

#include <stdio.h>

int main()
{
    printf("Hello world\n");

    return 0;
}

当然C++有⼀套自己的输入输出,C++中需要把定义文件代码后缀改为.cpp,vs编译器看到是.cpp就会调用C++编译器编译,linux下要用g++编译,不再是gcc。

严格说C++版本的hello world应该是这样写的:

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello world\n" << endl;

    return 0;
}

五、命名空间

(1)namespace的定义 

①定义命名空间,需要用到namespace关键字,后面跟着命名空间的名字,然后用一对{}连接,{}中为命名空间的成员。命名空间可以定义变量/函数/类型等。

与结构体的区别是:{}后面没有分号;

namespace zfy
{
    //变量
    int rand = 10;
    
    //函数
    int Add(int a, int b)
    {
        return a + b;
    }

    //类型
    struct Node
    {
        struct Node* next;
        int val;
    }
}

②namespace本质是定义出一个,这个域跟全局域各自独立

③不同的域可以定义同名变量,因此下面的两个rand不会冲突。

#include <stdio.h>
#include <stdlib.h>

namespace zfy
{
    int rand = 10;
    int Add(int a, int b)
    {
        return a + b;
    }
    struct Node
    {
        struct Node* next;
        int val;
    }
}

int main()
{
    printf("%p\n",rand);//这里默认访问的是全局的rand函数指针(即头文件stdlib.h中的)
    printf("%d\n",zfy::rand);//这里访问的是命名空间中的rand
    return 0;
}

④C++中域有函数局部域、全局域、命名空间域、类域

域影响的是编译时语法查找⼀个变量/函数/ 类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。

局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。

//全局域
int rand = 10;

//命名空间域
namespace zfy
{
    int rand = 10;
    int Add(int a, int b)
    {
        return a + b;
    }
    struct Node
    {
        struct Node* next;
        int val;
    }
}

//函数局部域
int Add(int a, int b)
{
    return a + b;
}


int main()
{
   //函数局部域
   int rand = 10;

    return 0;
}

⑤namespace只能定义在全局(即不能在main函数或者其他函数中定义),当然他还可以嵌套定义。

//命名空间的嵌套
namespace zfy
{
     
    namespace z
    {
        int rand = 1;
        int Add(int left, int right)
        {
            return left + right;
        }
    }
    
     namespace f
     {
         int rand = 2;
         int Add(int left, int right)
         {
             return (left + right)*10;
         }
     }
}

//调用
int main()
{
    printf("%d\n", zfy::z::rand);
    printf("%d\n", zfy::f::rand);
    printf("%d\n", zfy::z::Add(1, 2));
    printf("%d\n", zfy::f::Add(1, 2));

    return 0;
}

⑥项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。

// Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

namespace zfy
{
    typedef int STDataType;
    typedef struct Stack
    {
        STDataType* a;
        int top;
        int capacity;
    }ST;

    void STInit(ST* ps, int n);
    void STDestroy(ST* ps);
    void STPush(ST* ps, STDataType x);
    void STPop(ST* ps);
    STDataType STTop(ST* ps);
    int STSize(ST* ps);
    bool STEmpty(ST* ps);
}


// Stack.cpp
#include"Stack.h"
namespace zfy
{
    void STInit(ST* ps, int n)
    {
    assert(ps);
    ps->a = (STDataType*)malloc(n * sizeof(STDataType));
    ps->top = 0;
    ps->capacity = n;
    }
 
    // 栈顶 
    void STPush(ST* ps, STDataType x)
    {
        assert(ps);
        // 满了, 扩容 
        if (ps->top == ps->capacity)
        {
            printf("扩容\n");
            int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
            STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
            if (tmp == NULL)
            {
                perror("realloc fail");
                return;
            }
            ps->a = tmp;
            ps->capacity = newcapacity;
        }
        ps->a[ps->top] = x;
        ps->top++;
    }

}

比如说,我们在Stack.h和Stack.cpp中都定义了名为zfy的命名空间,但在使用时会被认为是一个命名空间,不会冲突:

// test.cpp
#include"Queue.h"
#include"Stack.h"

//在全局定义⼀份单独的Stack 
typedef struct Stack
{
    int a[10];
    int top;
}ST;

void STInit(ST* ps){}
void STPush(ST* ps, int x){}

int main()
{
    // 调⽤全局的 
    ST st1;
    STInit(&st1);
    STPush(&st1, 1);
    STPush(&st1, 2);
    printf("%d\n", sizeof(st1));
 
    // 调⽤zfy namespace的 
    zfy::ST st2;
    bzfy::STInit(&st2);
    zfy::STPush(&st2, 1);
    zfy::STPush(&st2, 2);
    printf("%d\n", sizeof(st2));
    
    return 0;
}

⑦C++标准库都放在⼀个叫std(standard)的命名空间中。

(2)命名空间的使用

编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。

#include<stdio.h>
namespace zfy
{
    int a = 0;
    int b = 1;
}

int main()
{
    // 编译报错:error C2065: “a”: 未声明的标识符 
    printf("%d\n", a);
    
    return 0;
}

因此我们要想使用命名空间中定义的变量/函数,有三种方式:

①指定命名空间访问。项目中推荐这种方式。

// 指定命名空间访问 
int main()
{
    printf("%d\n", zfy::a);
    
    return 0; 
}

②using将命名空间中某个成员展开。项目中经常访问的不存在冲突的成员推荐这种方式。

// using将命名空间中某个成员展开 
using zfy::b;

int main()
{
    printf("%d\n", zfy::a);
    printf("%d\n", b);
 
    return 0; 
}

③展开命名空间中全部成员。项目不推荐,冲突风险很大,因为可能会与全局中的变量冲突,导致编译报错,但在日常小练习时较方便,推荐使用。

// 展开命名空间中全部成员 
using namespce zfy;

int main()
{
    printf("%d\n", a);
    printf("%d\n", b);
 
    return 0; 
}

六、C++的输入和输出 

①<iostream>是Input Output Stream的缩写,是标准的输入、输出流库,定义了标准的输入、输 出对象。

②std::cin是 istream类的对象,它主要面向窄字符(narrow characters (of type char))的标准输输入流。

③std::cout 是ostream类的对象,它主要面向窄字符的标准输出流。

④std::endl是⼀个函数,流插入输出时,相当于插入⼀个换行字符刷新缓冲区

⑤>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)。

⑥使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是通过函数重载实现的),其实最重要的是 C++的流能更好的支持自定义类型对象的输入输出。

#include <iostream>
using namespace std

int main()
{
    int a = 0;
    double b = 0.1;
    char c = 'a';
    
    printf("%d %lf %c\n",a, b, c);

    //输出整型
    std::cout << a << std::endl;
    //输出浮点型
    std::cout << b << std::endl;
    //输出字符
    std::cout << c << std::endl;

    //还可以连接使用
    std::cout << a << " " << b << " " << c << " " << std::endl;
    //输出:0 0.1 a

    //输出换行符
    std::cout << "Hello\n";
    std::cout << a << '\n';
    std::cout << b << "\n";
    std::cout << c << std::endl;

    //在前面可以使用using namespace std;那么std::cout、std::endl可以写成cout、endl
    
    //输入时可以自动识别类型
    cin >> a;
    cin >> b >> c;
    
    //输出
    std::cout << a << std::endl;
    std::cout << b << " " << c << std::endl;

    return 0;
}

⑦  cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。 

⑧⼀般日常练习中我们可以using namespace std,实际项目开发中不建议using namespace std。

这里我们没有包含<stdio.h>,仍可以使用printf和scanf,其实是在包含<stdio.h>时间接包含了。vs系列编译器是这样的,其他编译器可能会报错。

#include<iostream>
using namespace std;

int main()
{
    // 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码,可以提⾼C++IO效率  
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    return 0;
}

七、缺省函数

①缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时,如果没有指定实参,则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省半缺省参数。(有些地方把缺省参数也叫默认参数);

#include <iostream>
#include <assert.h>
using namespace std;

//全缺省
void Func(int a = 0)
{
 cout << a << endl;
}

int main()
{
 Func(); // 没有传参时,使⽤参数的默认值 
 Func(10); // 传参时,使⽤指定的实参 
 return 0;
}

②全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。

③C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。

④带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。

#include <iostream>
using namespace std;

// 全缺省:全部形参给缺省值
void Func1(int a = 10, int b = 20, int c = 30)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl << endl;
}

// 半缺省:部分形参给缺省值
//半缺省函数必须从右往左依次连续缺省,不能跳跃给缺省值
//错误写法:void Fun2(int a = 10, int b , int c = 20)
void Func2(int a, int b = 10, int c = 20)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl << endl;
}

int main()
{
    Func1();
    Func1(1);
    Func1(1,2);
    Func1(1,2,3);
    //传参必须从左到右,不能跳跃给实参
    //错误写法:Fun1(,2,3);
    
    Func2(100);
    Func2(100, 200);
    Func2(100, 200, 300);

    return 0;
}

⑤函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省 值。

// Stack.h
#include <iostream>
#include <assert.h>
using namespace std;

typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST* ps, int n = 4);//半缺省


// Stack.cpp
#include"Stack.h"

// 缺省参数不能声明和定义同时给,必须声明时给缺省值
void STInit(ST* ps, int n)
{
    assert(ps && n > 0);
    ps->a = (STDataType*)malloc(n * sizeof(STDataType));
    ps->top = 0;
    ps->capacity = n;
}


// test.cpp
#include"Stack.h"
int main()
{
    ST s1;
    STInit(&s1);
    //如果不确定大小,默认为4
    //如果确定知道要插⼊1000个数据,初始化时⼀把开好,避免扩容 
    ST s2;
    STInit(&s2, 1000);
 
    return 0;
}

八、函数重载

①C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。(返回值不同不能作为重载条件)

#include<iostream>
using namespace std;

// 1、参数类型不同 
int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}

double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

// 2、参数个数不同 
void f()
{
    cout << "f()" << endl;
}

void f(int a)
{
    cout << "f(int a)" << endl;
}

// 3、参数类型顺序不同(本质上还是类型不同)
void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}

void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}

//!返回值不同不能作为重载条件,因为调⽤时也⽆法区分 
//void fxx()
//{}

//int fxx()
//{
// return 0;
//}

②这样C++函数调用就表现出了多态行为,使用更灵活。而C语言是不支持同⼀作用域中出现同名函数的。

// 下⾯两个函数构成重载 
// 但是调⽤f()时,会报错,存在歧义,因为编译器不知道调⽤谁 
void f1()
{
 cout << "f()" << endl;
}

void f1(int a = 10)
{
 cout << "f(int a)" << endl;
}

int main()
{
    Add(10, 20);
    Add(10.1, 20.2);
 
    f();
    f(10);
    f(10, 'a');
    f('a', 10);
    //到底是第一个函数,还是第二个函数,使用缺省值?(有歧义)
 
    return 0;
}

九、写在最后 

 我们刚接触C++,需要一定的知识储备才能进行学习。

敬请期待“入门基础(下)”~

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

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

相关文章

第46课 Scratch入门篇:狙击望远镜

无限画中画 故事背景: 手拿一把狙击枪,第一次按下空格键的时候瞄准镜放大一倍,再按一次再放大一倍。开枪设计,瞬间击毁! 程序原理: 1、瞄准的物品放大,其实是角色的变化,我们把背景设置成角色,原始的角色是 480360,第一次放大的图为 14401080,放大了 3 倍。第二级…

【Java 并发编程】(二) 从对象内存布局开始聊 synchronized

对象的内存布局 首先抛出一个经典面试题: 一个 Object 对象占多大? 这里我用工具打印了出来, 发现是 “16bytes”, 也就是 16B; 为什么? 请继续往下看; 普通对象(除了数组), 由markword, 类型指针, 实例数据(就是对象里的成员), 对齐填充(整个对象大小要能被8B整数, 方便6…

思科OSPF动态路由配置8

#路由协议实现# #任务八OSPF动态路由配置8# 开放式最短路径优先&#xff08;Open Shortest Path First,OSPF&#xff09;协议是目前网络中应用最广泛的动态路由协议之一。它也属于内部网关路由协议&#xff0c;能够适应各种规模的网络环境&#xff0c;是典型的链路状态路由协…

ZooKeeper 集群的详细部署

ZooKeeper 集群部署 一、ZooKeeper 简介1.1 什么是 ZooKeeper1.2 ZooKeeper 特点 二 ZooKeeper 的架构和设计4.1 ZooKeeper 数据模型4.1.1 Znode 节点特性 三、ZooKeeper 的集群安装前准备工作3.1 需要的准备工作3.2 Linux 系统 3 个节点准备3.2.1 克隆3.2.2 配置另外两台服务器…

【RabbitMQ】 相关概念 + 工作模式

本文将介绍一些MQ中常见的概念&#xff0c;同时也会简单实现一下RabbitMQ的工作流程。 MQ概念 Message Queue消息队列。是用来存储消息的队列&#xff0c;多用于分布式系统之间的通信。 系统间调用通常有&#xff1a;同步通信和异步通信。MQ就是在异步通信的时候使用的。 同…

高考志愿智能推荐系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

第三方软件测评中心分享:软件系统测试内容和作用

近年来&#xff0c;随着信息技术的迅猛发展&#xff0c;软件系统的应用范围不断扩大。保证软件质量的关键措施之一就是软件系统测试。软件系统测试是指在软件开发生命周期中&#xff0c;通过一系列特定的测试活动来验证和确认软件系统的性能、功能及安全性&#xff0c;确保软件…

优优嗨聚集团:餐饮合作新未来引领美食产业新风尚

在快速变化的21世纪&#xff0c;餐饮行业作为民生消费的重要组成部分&#xff0c;正经历着前所未有的变革与挑战。随着消费者需求的多元化、个性化以及科技的不断进步&#xff0c;餐饮合作的新模式正悄然兴起&#xff0c;为行业带来了前所未有的发展机遇与活力。本文将探讨餐饮…

【Redis】Redis 数据类型与结构—(二)

Redis 数据类型与结构 一、值的数据类型二、键值对数据结构三、集合数据操作效率 一、值的数据类型 Redis “快”取决于两方面&#xff0c;一方面&#xff0c;它是内存数据库&#xff0c;另一方面&#xff0c;则是高效的数据结构。 Redis 键值对中值的数据类型&#xff0c;也…

网页版IntelliJ IDEA部署

在服务器部署网页 IntelliJ IDEA 引言 大家好&#xff0c;我是小阳&#xff0c;今天要为大家带来一个黑科技——如何在云端部署和使用WEB版的IntelliJ IDEA&#xff0c;让你在任何地方都可以随心所欲地进行Java开发。这个方法特别适合那些用着老旧Windows电脑&#xff0c;部署…

MySQL集群+Keepalived实现高可用部署

Mysql高可用集群-双主双活-myqlkeeplived 一、特殊情况 常见案例&#xff1a;当生产环境中&#xff0c;当应用服务使用了mysql-1连接信息&#xff0c;在升级打包过程中或者有高频的数据持续写入【对数据一致性要求比较高的场景】&#xff0c;这种情况下&#xff0c;数据库连接…

Springboot 整合 Swagger3(springdoc-openapi)

使用springdoc-openapi这个库来生成swagger的api文档 官方Github仓库&#xff1a; https://github.com/springdoc/springdoc-openapi 官网地址&#xff1a;https://springdoc.org 目录题 1. 引入依赖2. 拦截器设置3. 访问接口页面3.1 添加配置项&#xff0c;使得访问路径变短…

贪吃蛇(C语言详解)

贪吃蛇游戏运行画面-CSDN直播 目录 贪吃蛇游戏运行画面-CSDN直播 1. 实验目标 2. Win32 API介绍 2.1 Win32 API 2.2 控制台程序&#xff08;Console&#xff09; 2.3 控制台屏幕上的坐标COORD 2.4 GetStdHandle 2.5 GetConsoleCursorlnfo 2.5.1 CONSOLE_CURSOR_INFO …

开源通用验证码识别OCR —— DdddOcr 源码赏析(一)

文章目录 [toc] 前言DdddOcr环境准备安装DdddOcr使用示例 源码分析实例化DdddOcr实例化过程 分类识别分类识别过程 未完待续 前言 DdddOcr 源码赏析 DdddOcr DdddOcr是开源的通用验证码识别OCR 官方传送门 环境准备 安装DdddOcr pip install ddddocr使用示例 示例图片如…

Wyn商业智能助力零售行业数字化决策高效驱动

最新技术资源&#xff08;建议收藏&#xff09; https://www.grapecity.com.cn/resources/ 项目背景及痛点 百利商业的业务覆盖赛格、 SKP、奥莱、王府井等多地区具有代表性的商场&#xff0c;并创立了多个自有品牌。随着新零售模式的兴起&#xff0c;百利商业紧跟时代步伐&am…

培训学校课程管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

web开发,过滤器,前后端交互

目录 web开发概述 web开发环境搭建 Servlet概述 Servlet的作用&#xff1a; Servlet创建和使用 Servlet生命周期 http请求 过滤器 过滤器的使用场景&#xff1a; 通过Filter接口来实现&#xff1a; 前后端项目之间的交互&#xff1a; 1、同步请求 2、异步请求 优化…

Mysql(三)---增删查改(基础)

文章目录 前言1.补充1.修改表名1.2.修改列名1.3.修改列类型1.4.增加新列1.5.删除指定列 2.CRUD3.新增(Create)3.1.单行插入3.2.指定列插入3.3.多行插入 4.数据库的约束4.1.约束的分类4.2.NULL约束4.3.Unique约束4.4.Default 默认值约束4.5.PRIMARY KEY&#xff1a;主键约束4.6.…

Facebook与区块链:社交网络如何融入去中心化技术

随着区块链技术的飞速发展&#xff0c;去中心化理念逐渐渗透到各个领域&#xff0c;社交网络也不例外。作为全球领先的社交平台&#xff0c;Facebook在这一趋势下开始积极探索区块链技术的潜力&#xff0c;希望利用这一前沿技术来提升平台的安全性、透明度和用户控制权。本文将…

景联文科技:一文详解如何构建高质量SFT数据

在图像处理和计算机视觉领域中&#xff0c;将一张图像转化为可用于训练机器学习模型的数据是一项复杂而重要的任务。SFT&#xff08;Supervised Fine-Tuning&#xff0c;监督微调&#xff09;是一种常见的深度学习策略&#xff0c;在这一过程中发挥着核心作用。 SFT是指在一个预…