C++:类和对象初识
C++:类和对象初识
- 前言
- 类的引入与定义
- 引入
- 定义
- 类的两种定义方法
- 1. 声明和定义全部放在类体中
- 2. 声明和定义分离式
- 类的成员变量命名规则
- 类的访问限定符及封装
- 访问限定符
- 封装
- 类的作用域与实例化
- 类的作用域
- 类实例化
- 实例化方式:
- 类对象模型
- 类对象的大小
- 存储方式
- this指针(重点)
- 引出
- 特性
- 深入理解
- 思考题?(重点中的重点)
- 解答情形1
- 解答情形2
- 原因分析
- 总结
前言
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
以我们洗衣服为例:
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
在面向对象编程(OOP)中,"类"和"对象"是最核心的概念。C++作为一门面向对象的语言,通过类和对象实现了数据抽象、封装、继承和多态等特性。理解类和对象的工作机制是掌握C++面向对象编程的关键。
类的引入与定义
引入
在C语言中,我们使用结构体(struct)来组织相关数据。C++在此基础上进行了扩展,允许结构体中不仅包含数据成员,还可以包含函数成员:
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。
//C++兼容C语言,同时C++中struct升级成了 类(具有类的所有特性)
struct _Stack {//成员方法void Init(int defaultCapacity = 4) {base = (int*)malloc(sizeof(int) * defaultCapacity);if (base == nullptr) {perror("malloc failed\n");return;}this->size = 0;this->capacity = defaultCapacity;}void Push() {//......}void Destroy() {free(this->base);this->base = nullptr;this->top = this->capacity;}//成员变量int* base;int top;int capacity;int size;
};
C++中结构体的名字,可以当成类名来使用。C++中的结构体具有class的所有功能(包括但不限于权限管理与this指针),只是成员的默认权限不同。
- 结构体中,
所有成员的默认访问权限是public
,结构体外可以直接访问 - C++的类中,
class所有成员的默认访问权限是private
, 不能再类外访问。
但更规范的C++做法是使用class关键字来定义类,它提供了更完善的访问控制机制。
定义
类定义的基本语法:
class className{// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
- class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
- 类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
示例:
class Clock {
private: // 访问限定符int hour;int minute;int second;public:void setTime(int h, int m, int s);void showTime() {cout << hour << ":" << minute << ":" << second << endl;}
};
类的两种定义方法
1. 声明和定义全部放在类体中
例如该类:
class Clock {
private: // 访问限定符int hour;int minute;int second;public:void showTime() {cout << hour << ":" << minute << ":" << second << endl;}
};
- 声明和定义全部放在类体中,需注意:成员函数如果在类体中定义,编译器可能会将其当成内联函数处理。(相当于在函数前加上了
inline
关键字,建议编译器使其称为内联函数)
2. 声明和定义分离式
//Stack.h
class Stack {
public:void Init(int defaultCapacity = 4);//在类内定义的函数,会直接建议编译器让该函数称为内联函数,void Push() { //类内定义的函数,不管加不加 inline ,都相当于加上了 inline //.......此处省略}void Pop() {//.......}
private:int* base;int top;int capacity;
};
//Stack.cpp
//类的声明和定义分离,需要在函数名前面,加上类的作用域限定
void Stack:: Init(int defaultCapacity) { //缺省参数一般在 函数声明 给出base = (int*)malloc(sizeof(int) * defaultCapacity);capacity = defaultCapacity;top = 0;
}
需要注意的是:
声明定义分离式
,如果函数有默认参数,一般要在函数声明处给出类的默认参数
一般情况下,更期望采用第二种方式, 因为我们并不希望所有的函数都称为内联函数。
类的成员变量命名规则
我们看该类:
class Clock {
private: // 访问限定符int hour;int minute;int second;public:void setTime(int hour, int minute, int second){// 这里的hour到底是成员变量,还是函数形参?hour = hour;}
};
我们的疑问如注释中所写,为了避免这样的矛盾,我们通常这样定义类的成员变量。
private: // 访问限定符int _hour;int _minute;int _second;
public:void setTime(int hour, int minute, int second){// 这里的hour到底是成员变量,还是函数形参?_hour = hour;//这样就解决了分歧,避免了二义性。}
类的访问限定符及封装
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
访问限定符
C++通过三个访问限定符实现封装:
public
:公有成员,类内外均可访问private
:私有成员,仅类内和友元可访问protected
:保护成员,介于两者之间(继承时使用)
特点:
public
修饰的成员在类外可以直接被访问。protected
和private
修饰的成员在类外不能直接被访问(此处protected和private是类似的)。- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
- 如果后面没有访问限定符,作用域就到}即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C语言)。
- C++需要兼容C语言,所以C++中struct可以当成结构体使用。
- 另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
- 注意:在继承和模板参数列表位置,struct和class也有区别,后序给大家介绍。
通常建议我们不要采用C++语法中的默认权限,不管是class还是struct,我们都应该手动控制访问权限。
封装
面向对象的三大特性:封装、继承、多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装是指将数据和操作数据的方法进行有机结合,并对外部隐藏实现细节。其优势体现在:
- 数据保护:通过私有化成员变量防止意外修改
- 接口统一:通过公有方法提供规范的操作方式
- 实现隔离:修改内部实现不影响外部使用
示例封装:
class BankAccount {
private:double balance; // 私有数据public:// 公开接口void deposit(double amount) {if(amount > 0) balance += amount;}bool withdraw(double amount) {if(amount <= balance) {balance -= amount;return true;}return false;}double getBalance() const { return balance; }
};
类的作用域与实例化
类的作用域
类定义了一个独立的作用域:
- 成员变量/函数的作用域在整个类体内(整个类内是一个整体)
- 外部访问需通过类名::成员或对象.成员
注意点:
- 如上文所讲,成员函数在类外定义时需要指定类域
void Clock::setTime(int h, int m, int s) {// 实现代码
}
我们可以对比一下各种作用域各有什么特点。
全局域
局部域
类作用域
命名空间域(使用时需要指定)
局部域和全局域会影响变量的生命周期, 类域和命名空间域不会影响生命周期
类实例化
用类类型创建对象的过程,称为类的实例化
- 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
int main(){person._age = 100; // 编译失败:error C2059: 语法错误return 0;
}
Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄。
做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
实例化方式:
Clock myClock; // 栈上分配
Clock* pClock = new Clock; // 堆上分配
new关键词我们将在后续讲解。
需要注意:
- 类声明不分配内存,实例化时才分配实际空间
- 同一类的不同对象拥有独立的成员变量存储空间
- 成员函数被所有对象共享(代码区存储)
类对象模型
类对象的大小
计算规则:
- 遵循结构体内存对齐原则
- 只计算成员变量大小(包括继承的)
- 空类大小为1字节(占位标识)
验证示例:
class Empty {};
class Data {int num; // 4字节double value; // 8字节char tag; // 1字节
};// 8 + 8 + 1 = 17 → vs下实际输出24(内存对齐)int main() {cout << sizeof(Empty) << endl;; // 输出1cout << sizeof(Data) << endl;; // 输出24
}
存储方式
类对象的存储采用分治策略:
- 成员变量:每个对象独立存储(栈区/堆区)
- 成员函数:所有对象共享代码区的一份拷贝
- 静态成员:存储在全局数据区
如图所示
这个模型在我们计算对象的大小时也有体现
:
- 对象中只存储成员变量。
- 成员函数存放在一个公共区域(成员函数不在对象内存储)
内存布局示例:
对象1: [成员变量区]
对象2: [成员变量区]
...
代码区: [成员函数]
结论:
- 一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐。
- 注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
结构体内存对齐规则
//结构体内存对齐规则
//1. 第一个成员在与结构体偏移量为0的地址处。
//2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
//注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
//VS中默认的对齐数为8
//3. 结构体总大小为:最大对齐数(所有变量类型最大者 与 默认对齐参数 取较小的那个)的整数倍。
//4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
//体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
this指针(重点)
引出
我们先定义一个日期类
class Date{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}int main(){Date d1, d2;d1.Init(2025,1,11);d2.Init(2024, 2, 22);d1.Print();d2.Print();return 0;
}
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
编译器隐式添加this指针参数:
// 编译器视角下的成员函数
//编译器编译后,会对成员函数进行处理,会给成员函数加上参数this, 访问变量时,使用this访问
void Init(Date* const this, int year, int month, int day){this->_year = year;this->_month = month;this->_day = day;}
void Print(Date* const this){cout <<this->_year<< "-" <<this->_month << "-"<< this->_day <<endl;
}int main(){Date d1, d2;//编译器视角下的函数调用,是编译器帮助我们传入的当前对象的地址d1.Init(&d1, 2025, 1, 11);d2.Init(&d2, 2024, 2, 22);d1.Print(&d1);d2.Print(&d2);return 0;
}
特性
- this指针的类型:
className* const
,const修饰指针本身,该指针不能被修改,也就是不能当左值。即成员函数中,不能给this指针赋值。 - 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。正因如此,上文计算对象大小的时候并没有计算this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
- this指针在函数内部是要反复调用的, vs的编译器对this指针传递做了优化,对象地址放在ecx寄存器中,exc中存储this指针的值
- this指针是函数的形参,因此this指针存放在内存的栈区中
深入理解
思考题?(重点中的重点)
有如下类和代码,思考?
class TestThis {
public:void Print() {cout << "Print" << endl;}void PrintA() {cout << _A << endl;}
private:int _A;
};
void Test_This_1() {TestThis* p = nullptr;p->Print();
}
void Test_This_2() {TestThis* p = nullptr;p->PrintA();
}
int main(){Test_This_1();//Test_This_2();return 0;
}
思考两个函数调用的结果分别是什么?
1.编译时报错 2. 运行时崩溃 3. 正常运行
我们依次调用来看:
解答情形1
我们可以看到,中断打印Print, 并且提示:进程已退出,代码为0
,0代表正常返回,可以看到,第一种情况的结果是:正常运行。
解答情形2
观察两张图可看到,图一,光标一直在闪动,图二,程序结束时,退出代码为**-1073741819**,光标一直在闪动且退出代码为负数,显然是运行时崩溃。
原因分析
class TestThis {
public:void Print() {cout << Print() << endl;}void PrintA() {cout << _A << endl;}
private:int _A;
};
先看该类,挺简单的。两个成员函数:
Print()
,打印字符串“Print”
PrintA()
,打印成员变量_A的值
再看两个调用:
void Test_This_1() {TestThis* p = nullptr;p->Print();
}
void Test_This_2() {TestThis* p = nullptr;p->PrintA();
}
说白了讲,p是TestThis对象的空指针,通过指针p,分别调用
Print()
和PrintA()
函数
class TestThis {
public:void Print() {//this指针为空,但函数内没有对this指针解引用cout << Print() << endl;}void PrintA() {//this指针为空,但函数内访问_A,本质是this->_A//成员变量在对象内,因此发生了this指针的解引用。cout << _A << endl;}
private:int _A;
};
void Test_This_1() {TestThis* p = nullptr;//表示this指针为空p->Print();
}
void Test_This_2() {TestThis* p = nullptr;//表示this指针为空p->PrintA();
}
两种情形下,this指针都是空的。
p->Print();
,p调用Print, 不会发生解引用。因为,由上文得,Print的地址并不在对象中,p会作为实参传递给this指针,并没有发生空指针的解引用。p->PrintA();
,p调用PrintA, 不会发生解引用。但PrintA()
函数内,有cout << _A << endl;
,本质上是cout << this->_A << endl;
,变量_A
存储在对象内,因此需要去对象中找,也就发生了对象指针的解引用。此时对象指针为空,那么对空指针解引用,也就发生了运行时崩溃。
总结
- 类与对象关系:类是蓝图,对象是实体
- 访问控制:通过public/private/protected实现封装
- 存储模型:对象独立存储数据,共享函数代码
- this机制:隐式指针实现对象自治
- 设计原则:高内聚低耦合,合理使用访问限定符
理解类和对象的工作机制是掌握C++面向对象编程的基础,后续的继承、多态等特性都是建立在此基础之上的深入扩展。正确使用类和对象可以有效提高代码的可维护性和复用性。
今天的分享到此结束了,各位大佬多多支持。
一键三连,好运连连!
相关文章:

C++:类和对象初识
C:类和对象初识 前言类的引入与定义引入定义类的两种定义方法1. 声明和定义全部放在类体中2. 声明和定义分离式 类的成员变量命名规则 类的访问限定符及封装访问限定符封装 类的作用域与实例化类的作用域类实例化实例化方式: 类对象模型类对象的大小存储…...

官网下载Redis指南
1.访问官网 https://redis.io/downloads/#stack 2.点击redis图标 拉到下面点击download 在新页面拉到最下面,点击install from source 找到需要的大版本后,点击releases page 最后点击下载需要的版本号即可...

活动预告 |【Part1】 Azure 在线技术公开课:迁移和保护 Windows Server 和 SQL Server 工作负载
课程介绍 通过 Microsoft Learn 免费参加 Microsoft Azure 在线技术公开课,掌握创造新机遇所需的技能,加快对 Microsoft 云技术的了解。参加我们举办的“迁移和保护 Windows Server 和 SQL Server 工作负载”活动,了解 Azure 如何为将工作负…...

【Linux系统编程】五、进程创建 -- fork()
文章目录 前言Ⅰ. 重温fork函数一、fork()的概念二、如何理解fork()有两个返回值 Ⅱ.fork的常规用法Ⅲ. fork调用失败的原因Ⅳ. 写时拷贝为什么存在写时拷贝❓❓❓ 前言 现阶段我们知道进程创建有如下两种方式,其实包括在以后的学习中这两种方式也是最常见的&#…...

深入解析 STM32 GPIO:结构、配置与应用实践
理解 GPIO 的工作原理和配置方法是掌握 STM32 开发的基础,后续的外设(如定时器、ADC、通信接口)都依赖于 GPIO 的正确配置。 目录 一、GPIO 的基本概念 二、GPIO 的主要功能 三、GPIO 的内部结构 四、GPIO 的工作模式 1. 输入模式 2. 输出模式 3. 复用功能模式 4. 模…...

深入探究 C++17 std::is_invocable
文章目录 一、引言二、std::is_invocable 概述代码示例输出结果 三、std::is_invocable 的工作原理简化实现示例 四、std::is_invocable 的相关变体1. std::is_invocable_r2. std::is_nothrow_invocable 和 std::is_nothrow_invocable_r 五、使用场景1. 模板元编程2. 泛型算法 …...

Vmware网络模式
一、Vmware虚拟网络 Vmware共支持创建20个虚拟网络,相当于现实生活的交换机,名称vmnet0-vmnet19 没创建一个虚拟网络。对应在物理机会自动生成相应的虚拟网卡 该虚拟网卡用于和对应的虚拟网络中的虚拟机通信 二、虚拟网络的工作模式 1、nat模式 …...

神经辐射场(NeRF):从2D图像到3D场景的革命性重建
神经辐射场(NeRF):从2D图像到3D场景的革命性重建 引言 在计算机视觉和图形学领域,如何从有限的2D图像中高效且准确地重建真实的3D场景,一直是一个重要的研究方向。传统的3D重建方法,如多视角几何、点云重建…...

深入解析AI技术原理
序言 在当今数字化时代,人工智能(AI)已经成为科技领域最炙手可热的话题之一。从智能家居到自动驾驶汽车,从医疗诊断到金融风险预测,AI的应用无处不在。然而,对于许多人来说,AI背后的技术原理仍然充满了神秘色彩。本文将深入探讨AI的核心技术原理,从基础理论到前…...

PDF 2.0 的新特性
近来闲来无事,就想着把PDF的新标准研究研究,略有所得,和大家分享一下。 PDF 2.0的主要新特性包括更高级的加密算法、改进的数字签名和权限管理机制、增强了对非罗马字符的支持,以及扩展了标签架构和3D建模语言“PRC”的支…...

Matlab机械手碰撞检测应用
本文包含三个部分: Matlab碰撞检测的实现URDF文件的制作机械手STL文件添加夹爪 一.Matlab碰撞检测的实现 首先上代码 %% 检测在结构环境中机器人是否与物体之间发生碰撞情况,如何避免? % https://www.mathworks.com/help/robotics/ug/che…...

(root) Additional property include:is not allowed
参考:执行docker compose命令出现 Additional property include is not allowed_(root) additional property include is not allowed-CSDN博客 原因是docker-compose的版本太低,下载最新的替换即可。 第一次2.6.x版本改成了2.19.x不够高,所…...

react 18父子组件通信
在React 18中,父子组件之间的通信方式与之前的版本基本相同,主要可以通过以下几种方式实现: 1. Props(属性) 父组件向子组件传递数据: 父组件通过属性(props)向子组件传递数据&am…...

FastReport 加载Load(Stream) 模板内包含换行符不能展示
如下代码 当以FastReport 载入streams时 当模板内包含换行符时会导致不能正常生成pdf System.Xml.XmlDocument newFrxXml new System.Xml.XmlDocument(); newFrxXml.Load(fileName);FastReport.Report report new FastReport.Report();using (var memStream new MemoryStre…...

Maven 中常用的 scope 类型及其解析
在 Maven 中,scope 属性用于指定依赖项的可见性及其在构建生命周期中的用途。不同的 scope 类型能够影响依赖项的编译和运行阶段。以下是 Maven 中常用的 scope 类型及其解析: compile(默认值): 这是默认的作用域。如果…...

vue3:点击子组件进行父子通信
问: 子组件怎么和爷爷组件通信 回答: 在Vue 3中,子组件和爷爷组件之间的通信可以通过事件冒泡和状态管理来实现。你可以使用Vue的事件系统来传递事件,或者使用全局状态管理库如Vuex或Pinia。以下是一个使用事件冒泡的示例&…...

Composo:企业级AI应用的质量守门员
在当今快速发展的科技世界中,人工智能(AI)的应用已渗透到各行各业。然而,随着AI技术的普及,如何确保其可靠性和一致性成为了企业面临的一大挑战。Composo作为一家致力于为企业提供精准AI评估服务的初创公司,通过无代码和API双模式,帮助企业监测大型语言模型(LLM)驱动的…...

Jackson扁平化处理对象
POJO对象 Data public class People {private PeopleInfo peopleInfo;private List<String> peopleIds;private Map<String, String> peopleMap;Datapublic static class PeopleInfo {private String name;private String address;} }JSON序列化处理 直接将对象进…...

Java即时编译器(JIT)的原理及在美团的实践经验
基本功 | Java即时编译器原理解析及实践 - 美团技术团队 这篇文章由美团AI平台/搜索与NLP部的珩智、昊天、薛超撰写,深入介绍了Java即时编译器(JIT)的原理及在美团的实践经验。 Java执行过程与即时编译器概述 Java执行过程:Java…...

使用 Ollama 在 Windows 环境部署 DeepSeek 大模型实战指南
文章目录 前言Ollama核心特性 实战步骤安装 Ollama验证安装结果部署 DeepSeek 模型拉取模型启动模型 交互体验命令行对话调用 REST API 总结个人简介 前言 近年来,大语言模型(LLM)的应用逐渐成为技术热点,而 DeepSeek 作为国产开…...

算法基础之八大排序
文章目录 概要1. 冒泡排序(Bubble Sort)2. 选择排序(Selection Sort)3. 插入排序(Insertion Sort)4. 希尔排序(Shell Sort)5. 归并排序(Merge Sort)6. 快速排…...

使用TensorFlow和Keras构建卷积神经网络:图像分类实战指南
使用TensorFlow和Keras构建卷积神经网络:图像分类实战指南 一、前言:为什么选择CNN进行图像分类? 在人工智能领域,图像分类是计算机视觉的基础任务。传统的机器学习方法需要人工设计特征提取器,而深度学习通过卷积神经…...

音频进阶学习十一——离散傅里叶级数DFS
文章目录 前言一、傅里叶级数1.定义2.周期信号序列3.表达式DFSIDFS参数含义 4.DFS公式解析1)右边解析 T T T、 f f f、 ω \omega ω的关系求和公式N的释义求和公式K的释义 e j ( − 2 π k n N ) e^{j(\frac{-2\pi kn}{N})} ej(N−2πkn)的释义 ∑ n 0 N − 1 e…...

20.<Spring图书管理系统①(登录+添加图书)>
PS:关于接口定义 接口定义,通常由服务器提供方来定义。 1.路径:自己定义 2.参数:根据需求考虑,我们这个接口功能完成需要哪些信息。 3.返回结果:考虑我们能为对方提供什么。站在对方角度考虑。 我们使用到的…...

关于图像锐化的一份介绍
在这篇文章中,我将介绍有关图像锐化有关的知识,具体包括锐化的简单介绍、一阶锐化与二阶锐化等方面内容。 一、锐化 1.1 概念 锐化(sharpening)就是指将图象中灰度差增大的方法,一次来增强物体的轮廓与边缘。因为发…...

Django开发入门 – 0.Django基本介绍
Django开发入门 – 0.Django基本介绍 A Brief Introduction to django By JacksonML 1. Django简介 1) 什么是Django? 依据其官网的一段解释: Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. …...

多智能体协作架构模式:驱动传统公司向AI智能公司转型
前言 在数字化浪潮的席卷下,传统公司的运营模式正面临着前所未有的挑战。随着市场竞争的日益激烈,客户需求的快速变化以及业务复杂度的不断攀升,传统公司在缺乏 AI 技术支撑的情况下,暴露出诸多痛点。在决策层面,由于…...

CentOS服务器部署Docker+Jenkins持续集成环境
一、准备工作 一台运行 CentOS 的服务器,确保有足够的磁盘空间、内存资源,并且网络连接稳定。建议使用 CentOS 7 或更高版本,本文以 CentOS 7 为例进行讲解。 拥有服务器的 root 权限,因为后续安装软件包、配置环境等操作需要较…...

【prompt实战】AI +OCR技术结合ChatGPT能力项目实践(BOL提单识别提取专家)
本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 1. 需求背景 2. 目标 3. BOL通用处理逻辑…...

【Android】Android开发应用如何开启任务栏消息通知
Android开发应用如何开启任务栏消息通知 1. 获取通知权限2.编写通知工具类3. 进行任务栏消息通知 1. 获取通知权限 在 AndroidManifest.xml 里加上权限配置,如下。 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android…...