1. 面向对象编程基础
1.1 面向过程与面向对象的本质区别
在编程范式的发展历程中,面向过程(Procedure Oriented)和面向对象(Object Oriented)是两种最基础的编程思想。让我们通过一个银行转账的案例来理解它们的差异:
面向过程实现:
cpp复制void transferMoney(float amount, int fromAccount, int toAccount) {
// 检查转出账户余额
float balance = getBalance(fromAccount);
if (balance < amount) {
printf("余额不足");
return;
}
// 执行转账操作
deduct(fromAccount, amount);
add(toAccount, amount);
// 记录交易日志
logTransaction(fromAccount, toAccount, amount);
}
面向对象实现:
cpp复制class Account {
public:
void transferTo(Account& toAccount, float amount) {
if (_balance < amount) {
throw InsufficientBalanceException();
}
_balance -= amount;
toAccount._balance += amount;
_transactionHistory.record(this, &toAccount, amount);
}
private:
float _balance;
TransactionHistory _transactionHistory;
};
关键差异分析:
-
代码组织方式:
- 面向过程:以函数为中心组织代码,数据与操作分离
- 面向对象:以对象为中心,数据与操作封装在一起
-
问题分解角度:
- 面向过程:关注"怎么做"——解决问题的步骤流程
- 面向对象:关注"谁来做"——问题中涉及哪些对象及它们的交互
-
扩展性对比:
- 面向过程:新增功能需要修改现有函数或添加新函数
- 面向对象:可通过继承、多态等方式扩展,对现有代码影响较小
实际开发建议:简单流程用面向过程,复杂系统用面向对象。现代C++开发中,通常采用面向对象为主,结合泛型编程的混合范式。
1.2 从C结构体到C++类的演进
C++的类概念并非凭空出现,而是从C语言的结构体逐步发展而来。让我们看一个栈(Stack)实现的演进过程:
C语言结构体版本:
cpp复制typedef struct {
int* data;
int capacity;
int top;
} Stack;
void stackInit(Stack* s, int cap) {
s->data = (int*)malloc(cap * sizeof(int));
s->capacity = cap;
s->top = -1;
}
void stackPush(Stack* s, int val) {
if (s->top == s->capacity - 1) {
printf("Stack overflow");
return;
}
s->data[++s->top] = val;
}
C++类版本:
cpp复制class Stack {
public:
Stack(int cap) : _capacity(cap), _top(-1) {
_data = new int[cap];
}
void push(int val) {
if (isFull()) throw std::runtime_error("Stack overflow");
_data[++_top] = val;
}
private:
int* _data;
int _capacity;
int _top;
};
关键改进点:
- 数据与操作的结合:成员函数可以直接访问成员变量,无需显式传递结构体指针
- 访问控制:通过public/private关键字实现封装
- 构造/析构:自动化的资源管理
- 异常处理:更现代的错误处理机制
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 类的核心特性详解
2.1 类的定义与成员组织
一个完整的类定义包含以下要素:
cpp复制class MyClass { // class关键字 + 类名
public: // 访问限定符
// 构造函数
MyClass(int x) : _x(x) {}
// 成员函数
void print() { std::cout << _x; }
private:
// 成员变量
int _x;
// 静态成员
static int count;
}; // 注意结尾的分号
成员函数的两种定义方式:
- 类内定义(隐式inline):
cpp复制class Vector {
public:
void push_back(int val) {
if (_size == _cap) reserve(_cap * 2);
_data[_size++] = val;
}
};
- 类外定义(需作用域解析):
cpp复制// Vector.h
class Vector {
public:
void push_back(int val);
};
// Vector.cpp
void Vector::push_back(int val) {
if (_size == _cap) reserve(_cap * 2);
_data[_size++] = val;
}
工程实践建议:头文件中只放声明,实现放在cpp文件中,避免重复定义问题。模板类除外,因其实现必须可见。
2.2 访问控制与封装
C++提供三种访问限定符:
| 限定符 | 类内访问 | 派生类访问 | 外部访问 |
|---|---|---|---|
| public | ✔ | ✔ | ✔ |
| protected | ✔ | ✔ | ✖ |
| private | ✔ | ✖ | ✖ |
封装的实际意义:
cpp复制class BankAccount {
public:
void deposit(float amount) {
if (amount <= 0) throw InvalidAmountException();
_balance += amount;
}
float getBalance() const { return _balance; }
private:
float _balance; // 防止外部直接修改导致不一致状态
};
重要规则:
- class默认private,struct默认public(为兼容C)
- 访问权限从限定符出现位置开始,直到下一个限定符或类结束
- 访问控制仅在编译期有效,运行时可通过指针操作绕过
2.3 类的作用域与实例化
类作用域的特殊性:
cpp复制class Logger {
public:
static Logger& instance() {
static Logger logger; // 静态局部变量
return logger;
}
void log(const std::string& msg);
private:
Logger() {} // 私有构造函数实现单例
};
// 类外定义必须指明作用域
void Logger::log(const std::string& msg) {
std::cout << msg << std::endl;
}
实例化过程解析:
cpp复制class Point {
public:
Point(int x, int y) : x(x), y(y) {
std::cout << "构造Point(" << x << "," << y << ")" << std::endl;
}
~Point() {
std::cout << "析构Point(" << x << "," << y << ")" << std::endl;
}
private:
int x, y;
};
void demo() {
Point p1(1, 2); // 栈上对象
Point* p2 = new Point(3, 4); // 堆上对象
delete p2; // 需手动释放
} // p1自动析构
关键点:
- 类定义不占内存,实例化后才分配空间
- 栈对象自动管理生命周期,堆对象需手动管理
- 成员变量的内
