类与对象的声明¶
从 class 语法开始,把对象思维落到可编译的 C++ 代码中。
类是用户自定义类型¶
整形、浮点型、字符型、布尔型是高级程序设计语言中常见的基本数据类型。 这些基本数据类型不足以表达复杂的数据结构和业务规则。 设计类就是设计一种新的类型。每个类就是面向对象程序的基本组成模块。
- 基本数据类型有明确规则,例如
int能参与算术运算。 - 自定义的类也应该有明确规则,例如一个
Clock对象应该如何被创建、修改和显示。
设计类的核心目标
一个良好设计的类能让调用者少记规则,少犯错误。
类的基本语法¶
class 内部可以声明数据成员和成员函数。
类的基本语法结构
语法要点
- 分号不能少:类定义结束后需要一个分号。
- 访问控制:
public和private决定类内成员对外是否可见。 - 成员:成员可以是数据,也可以是函数。
Clock 类第一版¶
先把状态和行为放进同一个类,再逐步增强规则。
Clock 类的声明与实现
关键设计点
public:外部可以访问或调用的接口。private:对象内部保存的状态和功能,外部不可直接访问。const成员函数:show()不应该修改对象状态,所以用const修饰,也称为常成员函数。
说明
public 和 private不仅可以用于修饰函数成员,也可以用于修饰数据成员。
但从封装的设计思想出发,应该尽量减少public所修饰的部分,仅对外公开需要公开的部分。
对象的声明¶
类是类型,对象是这个类型的具体实例。
创建和使用对象
类与对象的关系
- 同一数据类型:
c1和c2都是Clock类型的对象,即Clock类的实例。 - 独立内部状态:每个对象保存自己的
hour、minute、second。 - 共享功能逻辑:它们使用同一套成员函数定义。
成员访问运算符 “.”¶
通过对象名和点运算符调用公开成员函数。
通过对象调用公开接口
访问规则
- 公开接口:可以通过“对象名.成员名”调用
public成员函数。 - 私有成员:
private数据不能被类外直接访问。 - 设计意图:让外部通过对象允许的方式办事。
类作用域与成员可见性¶
理解类的作用域(Scope)和成员的可见性(Visibility),是正确设计和使用类的基础。
- 每一个类的定义都引入了一个独立的类作用域。在类作用域内声明的成员(包括数据成员和成员函数),其标识符在该作用域内是可见的。
- 成员函数体是类作用域的一部分,因此它可以直接访问该类中声明的任何成员,无论其访问权限是
public、private还是protected。
类内访问¶
在类定义的花括号 {} 内部,包括所声明的成员变量、成员函数等;成员函数可以直接访问本类的全部数据成员,不受成员访问权限的限制。
类作用域内的直接访问
类外访问:作用域与访问控制的共同作用¶
在类的作用域之外(即类定义的花括号 {} 外部,例如 main 函数或其他普通函数中),类的成员并非不可见,但其可见性和可访问性受到双重限制:
- 作用域运算符
:::在类外,需要通过“对象名.成员名”或“类名::静态成员名”的方式,在类的作用域中查找该成员。这是可见性的体现。 - 访问控制符:即使成员在类作用域内是可见的,类外代码也必须遵守
public、private和protected的访问控制规则。这是可访问性的规则。
类外访问的规则
可见性与访问控制的区别¶
为了更清晰地理解,可以将两者分开来看:
| 概念 | 核心含义 | 决定因素 | 成员函数内 | 类外(通过对象) |
|---|---|---|---|---|
| 可见性 (Visibility) | 能否通过名称引用该成员 | 作用域规则 | 全部可见 | 需要使用 . 或 :: 运算符查找 |
| 可访问性 (Accessibility) | 引用后,能否执行读写或调用操作 | 访问控制符 (public/private/protected) |
全部可访问 | 只能访问 public 成员 |
要点概括
- 访问控制不是为难调用者,而是保护对象不被错误使用。
- 对象内部知道如何管理自己,外部只需要知道如何请求服务。
思考题
在 C++ 中,成员函数通常在类定义的花括号 {} 之外实现,这些成员函数的函数体属于“类内”还是“类外”?
答案
成员函数的函数体虽然在词法上写在类外,但它仍然属于类的成员,因此在类的作用域内,具有类内访问权限,可以访问所有私有成员。
C/C++ 对比:公开字段 vs 私有字段¶
| C 写法(字段直接可见) | C++ 写法(通过接口修改) |
|---|---|
Clock c;c.hour = 8;c.minute = 99;c.second = 0; |
Clock c;// c.hour = 8; // 不能在类外直接访问c.setTime(8, 30, 0); |
| 风险:任何调用点都可能写入非法值。 | 收益:状态修改经过统一入口,可进行检查。 |
C 风格的风险
在 C 语言中,结构体字段完全公开,任何调用者都可以随意修改,导致对象状态可能被破坏。
C++ 风格的收益
在 C++ 中,将数据成员设为 private,强制外部通过 public 成员函数访问,从而在统一入口处进行合法性检查,保护对象状态的完整性。
设计原则:面向接口编程
正是由于类外代码对 private 成员“不可见且不可访问”,类的设计者才能自由地修改内部实现(例如改变数据存储方式),而不会影响使用该类的“外部客户代码”。这就是封装带来的核心价值——通过访问控制实现“接口与实现分离”。
- 外部视角:只需关注
public接口,了解“这个对象能做什么”。 - 内部视角:开发者专注于
private部分,确保“这个对象如何做好自己的工作”。
内联成员函数¶
非常简短的函数可以直接在类内定义。
内联成员函数
使用建议
- 适用场景:函数短小、逻辑简单、频繁调用。
- 保持简洁:不要在内联函数中包含复杂结构,如循环语句和
switch语句等。 - 重点理解:将函数体放在类声明中使用
inline关键字,理解写法即可,优化细节以后再谈。
案例改进:给 Clock 增加合法性检查¶
把检查逻辑放在修改状态的成员函数中。
带合法性检查的 setTime
改进要点
- 统一入口:所有时间修改都经过
setTime()。 - 返回结果:调用者可以根据
true/false处理错误。 - 类内访问:成员函数可以直接写私有数据。
小结¶
本节的核心是理解 类 如何作为自定义类型,将数据和行为封装在一起,并通过访问控制保护对象的内部状态。
class定义了对象的类型,对象是类型的实例。public成员是对象的对外接口,private成员是对象的内部实现。- 通过成员函数操作私有数据,实现规则集中管理。
- 类内初始值 和 内联函数 让代码更简洁、更安全。
下一步将进入 构造函数与对象初始化,解决对象“出生”时如何获得有效状态的问题。