面向对象特性
学习目标
通过本集的学习,你将能够:
- 理解封装的好处
- 掌握继承的利弊
- 理解多态的实现
- 学会使用接口与抽象类
1. 封装
1.1 什么是封装?
封装将数据和操作封装在一起,隐藏内部细节。
封装示例:
class BankAccount {
private:
double balance; // 私有,外部无法直接访问
public:
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
double get_balance() {
return balance;
}
};1.2 封装的好处
封装的优点:
1. 数据保护:防止非法访问
2. 简化接口:只暴露必要的方法
3. 易于修改:内部实现可以改变
4. 可重用性:独立的模块1.3 访问控制
访问修饰符(Java):
- public: 任何人都可访问
- protected: 子类和同包可访问
- private: 只有自己可访问
- (default): 同包可访问
访问修饰符(C++):
- public: 公开
- protected: 受保护
- private: 私有2. 继承
2.1 什么是继承?
继承让一个类继承另一个类的属性和方法。
继承示例:
class Animal {
public:
void eat() {
cout << "Eating" << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "Barking" << endl;
}
};
Dog d;
d.eat(); // 继承自 Animal
d.bark(); // 自己的方法2.2 继承的好处
继承的优点:
1. 代码复用:避免重复代码
2. 层次结构:组织类的关系
3. 多态基础:支持多态2.3 继承的问题
继承的缺点:
1. 紧耦合:子类依赖父类实现
2. 脆弱基类:修改父类可能破坏子类
3. 继承膨胀:层次过深
4. 不够灵活:运行时无法改变2.4 组合优于继承
优先使用组合:
class Engine {
public:
void start() { ... }
};
class Car {
private:
Engine engine; // 组合,不是继承
public:
void start() {
engine.start();
}
};3. 多态
3.1 什么是多态?
多态让不同对象对同一消息有不同响应。
多态示例:
class Shape {
public:
virtual double area() = 0; // 纯虚函数
};
class Circle : public Shape {
private:
double r;
public:
Circle(double r) : r(r) {}
double area() override {
return 3.14 * r * r;
}
};
class Rectangle : public Shape {
private:
double w, h;
public:
Rectangle(double w, double h) : w(w), h(h) {}
double area() override {
return w * h;
}
};
// 使用
vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));
for (Shape *s : shapes) {
cout << s->area() << endl; // 多态调用
}3.2 多态的实现
动态绑定(虚函数表):
每个有虚函数的类有一个 vtable
每个对象有一个 vptr 指向 vtable
调用时:
1. 通过 vptr 找到 vtable
2. 找到对应的函数指针
3. 调用函数4. 接口与抽象类
4.1 抽象类
抽象类不能实例化,包含抽象方法。
抽象类(Java):
abstract class Shape {
abstract double area(); // 抽象方法
void print() { // 普通方法
System.out.println("Shape");
}
}4.2 接口
接口定义契约,只有方法签名。
接口(Java):
interface Drawable {
void draw();
}
interface Resizable {
void resize(double factor);
}
class Circle implements Drawable, Resizable {
public void draw() { ... }
public void resize(double factor) { ... }
}4.3 抽象类 vs 接口
抽象类:
- 可以有构造函数
- 可以有非抽象方法
- 可以有字段
- 单继承
接口:
- 不能有构造函数
- 只有抽象方法(Java 8+ 可有默认方法)
- 只有常量
- 多实现5. 实用案例
5.1 案例1:封装银行账户
class BankAccount {
private double balance;
public BankAccount(double initial) {
balance = initial;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
public double getBalance() {
return balance;
}
}5.2 案例2:多态形状
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r * self.r
class Rectangle(Shape):
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
shapes = [Circle(5), Rectangle(4, 6)]
for s in shapes:
print(s.area())6. 自测问题
- 封装的好处是什么?
- 继承有什么优缺点?
- 什么是多态?
- 抽象类和接口的区别是什么?
- 为什么说组合优于继承?
7. 下集预告
下一集我们将学习异常处理机制!
参考资料
- 《设计模式》(GoF)
- 《程序设计语言:概念与构造》