类和对象
本节整理类与对象的核心概念:ADT(抽象数据类型)、this 指针、成员与访问方式、对象内存布局、成员函数的外联/内联实现、封装与静态成员等。
1. ADT 与类的组成
- 抽象数据类型(ADT):由数据和对数据的操作组成(数据成员 + 成员函数)。
- 类是 ADT 在 C++ 中的表现形式,包含数据成员和成员函数(行为)。
示例:
class Counter {
int value{};
public:
void inc() { ++value; }
int get() const { return value; }
};
2. this 指针
- 在非静态成员函数内部,
this
指向当前对象(类型为T* const
或const T* const
于const
成员函数)。 this
不能用于静态成员函数。
示例:
struct S { int x; void set(int v){ this->x = v; } };
3. 类的定义与组织
- 类通常放在头文件(.h/.hpp),成员函数实现放在源文件(.cpp),可以使用前置声明(forward declaration)降低编译依赖。`
- 使用
#pragma once
或 include guard 防止重复包含。
示例:
// foo.h
#pragma once
class Bar;
class Foo { Bar* b; public: void f(); };
4. 成员与对象访问
- 数据成员:普通类型、数组、指针、引用、其它类类型;
- 成员函数可以通过对象、指针或引用调用:
obj.f()
,p->f()
,r.f()
。
面向对象编程强调通过接口(方法)与对象交互,具体实现由对象本身决定(多态时行为在运行时可能不同)。
5. 对象的存储大小与对齐
- 对象大小受非静态数据成员的类型与顺序、对齐规则、以及是否含虚函数(通常添加 vptr)影响;
- 成员函数、静态成员与访问控制不会改变对象实例大小;
- 空类至少占 1 字节以保证不同对象有不同地址。
对齐规则(概念性):成员按其对齐要求放置,结构整体大小对齐到最大成员的对齐值。不同编译器/平台可能差异,需以 sizeof
和编译器文档为准。
6. 成员函数:外联实现与内联实现
- 外联实现:在类外定义成员函数(通常放在 .cpp),有外部链接。优点:隐藏实现、减少头文件体积;
- 内联实现:在类体内定义或用
inline
指定,允许编译器在调用处展开代码(仅建议用于小函数或模板)。
示例:
// 外联
// foo.h
class Foo{ public: void f(); };
// foo.cpp
// void Foo::f(){ /* ... */ }
// 内联(可放在头文件)
class Small{ public: int id() const { return 42; } };
注意:C++ 中 inline
更重要的语义是允许跨编译单元多重定义(只要定义相同),而非强制内联展开。
7. 封装与信息隐藏
- 封装(encapsulation):将数据与操作打包为整体,降低外部依赖;
- 信息隐藏(information hiding):通过
public
/protected
/private
控制可见性,尽量暴露稳定接口,隐藏实现细节以降低耦合。
设计建议:使用最小可见性原则(最少公开),通过接口隔离实现细节;对跨模块接口,暴露稳定、简单的函数签名。
8. 常量成员函数与 this 的类型
- 常成员函数(
T::f() const
)中,this
的类型为const T* const
;因此只能调用其它const
函数或访问可变成员。
示例:
struct R{ int get() const { return 0; } };
9. 静态成员(类变量与类方法)
- 静态数据成员属于类本身,所有实例共享;非内联静态成员需在类外定义并初始化(C++17 起可用
inline static
在类内初始化); - 静态成员函数没有
this
,不能直接访问非静态成员。
示例:
struct C{ static int cnt; static void inc(){ ++cnt; } };
int C::cnt = 0;
10. 小结
- 通过前置声明与把实现放入源文件可以降低编译耦合;
- 使用初始化列表、
const
成员函数以及合适的访问控制能提高类型的鲁棒性; - 明确静态/非静态语义,优先使用智能指针与 RAII 管理资源。
评论区 - 03_Class_and_Object