类与对象
class 把数据(成员)与操作(方法)封装在一起。struct 与 class 几乎相同,仅默认访问权限不同(struct 默认 public)。
class Circle {
double r_;
public:
double area() const { return 3.14159 * r_ * r_; }
void setRadius(double r) { r_ = r; }
};构造与析构
构造函数初始化对象,析构函数在对象离开作用域/删除时调用。用成员初始化列表更高效(避免先默认构造再赋值)。
class Widget {
int id_;
public:
Widget(int id) : id_(id) {} // 初始化列表
~Widget() { /* 清理 */ }
};可用 = default 让编译器生成默认实现,= delete 禁用拷贝(如唯一所有权对象),明确意图。
访问控制
public 对外接口、private 内部细节、protected 对子类可见。封装的核心是隐藏不变量的实现细节。
class Bank {
double balance_ = 0; // private 内部状态
public:
void deposit(double a) { if (a > 0) balance_ += a; }
double balance() const { return balance_; }
};继承
继承表达"is-a"。派生类复用基类接口,: public Base 表示公有继承。基类析构函数应为 virtual(见多态)。
class Shape {
public:
virtual double area() const = 0; // 纯虚函数,抽象类
virtual ~Shape() = default;
};
class Square : public Shape {
double s_;
public:
double area() const override { return s_ * s_; }
};多态与虚函数
用基类指针/引用调用虚函数时,运行时按对象实际类型分派(动态绑定)。override 关键字让编译器检查是否真重写了基类虚函数。
void printArea(const Shape& s) {
std::cout << s.area(); // 运行时按实际类型调用
}
Square sq{3};
printArea(sq); // 输出 9通过基类指针 delete 派生对象时,若基类析构非 virtual,只会调基类析构,导致派生部分泄漏。基类若要被继承,析构用 virtual。
组合优先
相比继承,"has-a"的组合更灵活、耦合更低。优先用成员对象组合行为,仅在确有"is-a"语义时继承。
class Engine { /* ... */ };
class Car {
Engine engine_; // 组合:Car has-an Engine
};RAII
Resource Acquisition Is Initialization:资源在构造函数获取、析构函数释放,生命周期绑定到作用域,异常安全且不易泄漏。智能指针、锁、文件句柄都依此设计。
{
std::ofstream f("a.txt"); // 构造即打开
f << "hi";
} // 离开作用域自动关闭(析构)即使中间抛出异常,栈展开也会调用局部对象析构,RAII 保证资源被释放——这是 C++ 资源管理的中枢思想。
常见陷阱与 FAQ
只要类可能被通过基类指针/引用析构(即被继承且有多态用法),基类析构就应为 virtual,否则派生部分不释放。
显式标注告诉编译器:这是重写。若基类没有对应的虚函数(签名不符),编译器会报错,避免"本想重写却变成重载/新函数"的隐蔽 bug。
能用组合表达就别继承。继承暴露基类内部、造成紧耦合;组合通过接口协作,更易测试与替换。