class是资源生命周期的契约起点,应从具体问题出发明确职责而非堆砌语法特性,优先用栈对象、确保构造/析构建立并守护不变量,避免裸指针与随意getter/setter,善用编译错误定位设计缺陷。

class 不是语法糖,是资源生命周期的契约起点。
你不需要先背完封装/继承/多态再写第一行 class。真实入门路径是:从一个具体问题出发,用 class 把“谁负责管什么”说清楚,而不是把“怎么算”塞进去。
先写一个不带虚函数、不继承、不重载的 class
新手最容易卡在“我要不要设计基类”。答案通常是:不要。90% 的初学场景根本用不到多态。
比如你要实现一个 Timer,目标只是调用 start() 和 elapsed() 就能拿到毫秒数——那就只写一个类,成员变量全 private,构造函数确保初始状态合法,其他函数只暴露必要接口:
- 别一上来就加
virtual或override,它们解决的是“运行时类型不确定”的问题,而你此刻连类型都只有一个 - 别急着拆成
ITimer和ConcreteTimer,抽象接口是为替换而生的,不是为“看起来像OOP”而生的 - 优先用栈对象(
Timer t;),而不是new Timer,避免过早引入内存管理干扰判断
构造函数和析构函数必须回答两个问题
每个 class 的构造函数不是“初始化字段”,而是“建立不变量”;析构函数不是“清理内存”,而是“守约收尾”。
比如一个 FileReader 类:
- 构造函数里打开文件失败,就直接抛异常(或用
std::optional包装),不留下“半初始化对象” - 析构函数里必须关文件句柄——哪怕用户忘了调
close(),RAII 机制也得兜底 - 如果类里有裸指针(如
char*),立刻停住:要么换成std::string或std::vector,要么补全拷贝/移动语义(否则传值就崩)
getter/setter 是危险信号,不是封装完成标志
出现 getBalance() + setBalance() 组合,基本说明这个类还没想清楚职责。
银行账户不该允许外部任意设余额,而应提供 deposit() 和 withdraw() 这样的行为接口,由类内部校验逻辑(比如余额不能为负)。
- 如果 getter 返回的是内部引用(如
const std::string& name() const),要确认返回值不会因后续修改失效 - 如果 setter 接受裸指针或 C 风格字符串(如
void setName(const char*)),它大概率会引发内存泄漏或悬垂指针 - 真正需要暴露数据时,优先返回值(
std::string name() const),而不是引用或指针
编译报错比运行崩溃更值得庆祝
C++ 的很多面向对象错误在编译期就能拦住,比如:
-
error: use of deleted function 'X::X(const X&)'—— 说明你用了禁用的拷贝,赶紧检查是否有std::unique_ptr或自定义析构函数 -
undefined reference to 'vtable for Y'—— 类里有虚函数但没定义析构函数,或者纯虚函数没在派生类里实现 -
no matching function for call to 'Z::Z()'—— 构造函数被你显式声明了带参版本,编译器不再生成默认无参构造,而某处又写了Z z;
这些错误信息直指设计断点,比程序跑起来后内存越界、野指针难查十倍。别绕开它们,就地读、改、测。 真正卡住入门进度的,从来不是“不懂虚函数表布局”,而是写完一个
class 后不敢问:“这个对象创建之后,谁保证它不会被误用?谁负责在它不用时把它收拾干净?” 把这个问题盯住,比记十个定义实在得多。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











