C++_类和对象
类
类(class)是 C++ 中定义用户自定义类型的工具。
类把:
- 数据(成员变量)
- 操作数据的函数(成员函数)
封装在一起。
类的核心思想:
- 数据抽象:只暴露“能做什么”,不强调“怎么做”
- 封装:把实现细节隐藏起来,只开放必要接口
类的基本概念
一个类通常包含两部分:
- 接口:提供给用户使用的操作
- 实现:数据成员、内部逻辑、私有辅助函数
使用类时,应关注:
- 这个类能做什么
- 如何正确调用它的接口
而不是它内部如何实现。
类的定义
类定义描述了:
- 这个类型有哪些成员
- 这个类型支持哪些操作
基本形式:
1 | class MyClass { |
说明:
- 类定义以
;结束 - 成员可以是:
- 数据成员
- 成员函数
- 类型别名
- 常量等
class 与 struct
二者都能定义类,区别主要在默认访问权限:
class:默认privatestruct:默认public
例如:
1 | class A { |
对象的定义和使用
类定义好后,就可以用它创建对象。
1 | class Sales_data { |
说明:
- 类名就是一种类型名
- 用类定义的变量叫对象
访问成员:
1 | item.bookNo = "C++"; |
若是指针:
1 | Sales_data *p = &item; |
成员函数与 this
成员函数是定义在类中的函数,用于操作类对象。
1 | class Person { |
this 指针
每个非静态成员函数都隐式带有一个 this 指针,指向调用该函数的对象。
例如:
1 | class A { |
理解:
- 在成员函数中直接访问成员,实际上是通过
this访问 this的类型通常可理解为指向当前对象的指针
const 成员函数
若成员函数不会修改对象状态,应在函数末尾加 const:
1 | class Person { |
含义:
- 该函数承诺不修改对象的数据成员(除
mutable成员外) const对象、const引用、const指针只能调用const成员函数
构造函数
构造函数用于初始化对象。
特点:
- 名字与类名相同
- 没有返回类型
- 对象创建时自动调用
- 可以重载
例如:
1 | class MyClass { |
构造函数初始化列表
推荐使用初始化列表初始化成员:
1 | class MyClass { |
作用:
- 在对象真正创建时直接初始化成员
- 比“先默认初始化,再在函数体中赋值”更高效、也更规范
为什么初始化列表重要
以下成员必须用初始化列表初始化:
const成员- 引用成员
- 没有默认构造函数的类类型成员
例如:
1 | class A { |
成员初始化顺序
成员初始化顺序由“成员声明顺序”决定,不由初始化列表顺序决定。
例如:
1 | class A { |
因此建议:
- 初始化列表顺序与成员声明顺序保持一致
默认构造函数
默认构造函数指不需要实参就能调用的构造函数。
例如:
1 | class A { |
编译器生成的默认构造函数
只有当类没有声明任何构造函数时,编译器才会自动生成默认构造函数。
一旦你自己定义了其他构造函数,编译器通常不再自动生成默认构造函数。
例如:
1 | class A { |
= default
如果想保留编译器默认行为,可以显式要求生成:
1 | class A { |
委托构造函数
一个构造函数可以调用同类中的另一个构造函数,这叫委托构造函数。
1 | class A { |
说明:
- 委托构造函数的初始化列表中只能写类名本身
- 实际初始化工作由被委托的构造函数完成
优点:
- 减少重复代码
- 统一初始化逻辑
对象的初始化方式
类对象常见初始化方式:
1 | A a; // 默认初始化 |
注意:
A b();是经典易错点:它声明了一个函数- 列表初始化可避免某些窄化转换
访问控制与封装
C++ 用访问说明符控制成员可见性:
public:对外可访问,构成类的接口private:仅类内部可访问,隐藏实现protected:主要用于继承(基础阶段可先了解)
例如:
1 | class A { |
封装的意义:
- 防止外部随意改内部状态
- 降低耦合
- 便于后续修改实现而不影响使用者
友元
类可以授权某些函数或类访问自己的非公有成员,这些被授权者叫友元。
友元函数
1 | class MyClass { |
特点:
- 友元不是类成员
- 但能访问类的
private/protected成员
友元类
1 | class B; |
表示 B 的成员函数可以访问 A 的非公有成员。
友元的特点
- 友元声明写在类内
- 不受所在访问说明符位置影响
- 友元关系不传递
- 友元关系不继承
- 若是一组重载函数,需分别声明为友元
类与 const
const 成员函数
常量对象只能调用常量成员函数:
1 | class A { |
若成员函数未加 const,就不能被 const 对象调用。
返回 *this
成员函数可以返回 *this,常见于链式调用:
1 | class A { |
若用于常量对象,通常还会写 const 重载版本。
类的静态成员
静态成员属于类本身,不属于某个具体对象。
分为:
- 静态数据成员
- 静态成员函数
静态数据成员
1 | class Account { |
特点:
- 所有对象共享同一份静态成员
- 不存储在对象内部
- 生命周期贯穿整个程序
静态成员函数
1 | class Account { |
特点:
- 不依赖具体对象
- 没有
this指针 - 不能声明为
const - 不能直接访问非静态成员
访问静态成员
推荐用类名访问:
1 | double r = Account::getRate(); |
也可以用对象或指针访问,但本质上还是类成员:
1 | Account a; |
静态数据成员的定义
静态数据成员通常需要在类外定义一次:
1 | double Account::rate = 0.05; |
注意:
- 类内只是声明
- 类外才是定义
- 不能再写
static
类内初始化静态成员
对于某些静态常量整型成员,可以类内初始化:
1 | class A { |
现代 C++ 中,也常见:
1 | class A { |
类的声明与前向声明
类可以先声明、后定义:
1 | class A; // 前向声明 |
此时 A 是不完全类型。
不完全类型能做的事很有限,常见允许:
- 声明指向它的指针或引用
- 声明以它为参数/返回值的函数
不能做的事:
- 定义该类型对象
- 访问其成员
- 知道其大小
例如:
1 | class A; |
类作用域
每个类有自己的作用域。
在类外访问成员时通常要借助:
./->访问普通成员::访问类作用域中的名字
类外定义成员函数时要加类名限定:
1 | class A { |
类内定义与类外定义
定义在类内的成员函数通常是隐式 inline 的。
1 | class A { |
若函数较复杂,通常在类外定义,更清晰:
1 | class A { |
隐式类类型转换
如果类有一个只接受一个实参的构造函数,那么它可能定义了一种从该参数类型到类类型的隐式转换。
例如:
1 | class A { |
这种构造函数常称为转换构造函数。
explicit
为了禁止这种隐式转换,可把构造函数声明为 explicit:
1 | class A { |
效果:
1 | A a(10); // 正确,直接初始化 |
说明:
explicit主要用于单参数构造函数- 只能阻止隐式转换,不影响直接初始化
聚合类
聚合类是一种可直接用花括号初始化成员的简单类。
典型条件(基础复习可先记这版):
- 所有成员都是
public - 没有用户定义的构造函数
- 没有虚函数、虚基类等复杂特性
例如:
1 | struct Data { |
特点:
- 初始化顺序与成员声明顺序一致
- 适合简单数据集合
字面值常量类
若一个类能用于常量表达式相关场景,就可能是字面值常量类。
复习时先记核心条件:
- 数据成员类型本身得适合常量表达式
- 类通常需要至少一个
constexpr构造函数 - 构造过程要足够简单、可在编译期求值
constexpr 构造函数
构造函数也可以是 constexpr:
1 | class Debug { |
作用:
- 可用于构造
constexpr对象 - 成员初始化必须满足常量表达式要求
定义抽象数据类型
类最常见的用途之一,就是定义抽象数据类型(ADT):
- 对外只暴露必要操作
- 内部数据受保护
- 保证对象始终处于合法状态
设计类时建议:
- 先想清楚“对象表示什么”
- 再确定“对象能做什么”
- 最后决定“哪些数据该隐藏”
一个完整的小例子
1 | class Sales_data { |
这个类体现了:
- 数据成员放在
private - 接口放在
public - 用构造函数控制初始化
- 用
const成员函数保证只读操作 - 返回
*this支持链式调用
易错点总结
class 默认是 private,struct 默认是 public
构造函数没有返回类型
包括 void 也不能写。
成员初始化顺序看“声明顺序”,不看初始化列表顺序
const 成员、引用成员必须在初始化列表中初始化
一旦自定义了构造函数,编译器通常不再自动生成默认构造函数
若需要,显式写:
1 | A() = default; |
const 对象只能调用 const 成员函数
静态成员函数没有 this
因此:
- 不能访问非静态成员
- 不能声明为
const
静态数据成员通常要在类外定义一次
1 | double Account::rate = 0.05; |
友元不是成员
只是“被授权访问”。
单参数构造函数可能触发隐式转换
若不希望这样,使用 explicit
前向声明只能声明指针/引用,不能定义对象
1 | class A; |




