面向对象编程(OOP)的三大核心特性——封装、继承和多态
基础概念:类与对象
-
类:是一个大类的抽象集合,如(动物,汽车),它定义了这个类的共有属性(变量)和行为(函数)。如汽车可以包含能源,颜色等变量以及启动,刹车等函数。
-
对象:是类中集合的一个具体实现,如一辆黑色特斯拉电车
封装
封装的核心思想将数据和对数据的操作封装成一个类,只能通过接口进行访问
-
实现机制:在C++中,封装主要通过类以及三种访问修饰符来实现:
-
private:私有成员,仅在类内部可以访问。 -
protected:保护成员,可在类内部和派生类中访问。 -
public:公有成员,任何地方都可以访问。
-
-
核心目的与好处
-
数据保护与安全:将关键数据成员设为
private,防止外部代码直接随意修改,保证了对象内部状态的一致性和合法性。外部只能通过类提供的公共接口(公有成员函数)来访问或修改数据,接口中可以加入必要的验证逻辑。 -
隐藏实现细节:使用者只需知道类提供了哪些接口以及接口的用途,而无需关心内部复杂的实现。这使得代码更易于使用和维护。
-
降低耦合度:模块之间通过清晰的接口交互,减少了相互依赖,使得修改类的内部实现不会影响到使用它的其他代码。
-
class BankAccount {
private: // 数据被隐藏和保护
std::string accountNumber;
double balance;
public: // 公开的接口
BankAccount(std::string accNum, double initialBalance)
: accountNumber(accNum), balance(initialBalance) {}
// 通过公共方法操作数据,确保安全性
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
double getBalance() const { // 提供读取接口
return balance;
}
// 无法直接从外部设置账户号和余额,保证了数据安全
};
继承
继承即依据一个已有的类建立一个新的类,新的类具有原来的类的属性和行为,并且可以增加自己特有的属性和行为。。其中,继承方式决定了基类成员在派生类中的访问权限,主要分为public、protected和private三种。
|
基类中的原始访问权限 |
在 |
在 |
在 |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
不可见 |
不可见 |
不可见 |
在内存中,派生类对象包含其基类的成员副本(基类部分通常位于对象内存布局的前部),然后才是派生类自己的新成员。这使得基类指针或引用可以指向派生类对象。
// 基类
class Animal {
public:
void eat() { std::cout << "Eating...\n"; }
};
// 派生类 (public继承)
class Dog : public Animal {
public:
void bark() { std::cout << "Woof!\n"; }
};
int main() {
Dog myDog;
myDog.eat(); // 继承自Animal的方法
myDog.bark(); // Dog自己的方法
return 0;
}
-
public继承:基类的接口(public)和受保护的部分(protected)完全传递给派生类及其后代。 -
protected继承:基类的接口被"收回"到派生类的家族内部(变为protected),只能在家族内传递,不对外公开。 -
private继承:基类的所有都被"吸收"为派生类的私有实现细节(变为private),传递就此终止,后代也无法使用。
选择建议:
-
绝大多数情况下,应使用
public继承,因为它准确地表达了"是一个"的逻辑关系,符合面向对象的设计原则。 -
当需要复用代码但又想隐藏基类接口时,优先考虑使用组合(将一个类的对象作为成员),这通常比
private或protected继承更灵活、更清晰。 -
仅在非常特殊的情况下,例如需要重写基类的虚函数或访问其保护成员,且不希望暴露基类接口时,才考虑使用
private继承。protected继承在实践中极为罕见。
多态
多态意为"多种形态"。它允许使用统一的接口来处理不同类型的对象,从而根据对象的实际类型执行不同的操作。多态分为静态多态和动态多态。
-
静态多态(编译时多态)
在编译期就确定了具体要调用的函数。
-
函数重载:在同一作用域内,函数名相同但参数列表不同(类型、个数、顺序)。
-
运算符重载:赋予已有运算符新的功能,用于自定义类型。
-
模板:编写与类型无关的通用代码。
-
-
动态多态(运行时多态)
在程序运行时才能确定调用哪个函数。这是通过虚函数实现的。
-
虚函数:在基类中使用
virtual关键字声明的成员函数。派生类中可以对其进行重写(使用override关键字是良好实践)。 -
实现机制:虚函数表(vtable)和虚表指针(vptr)
-
编译器会为每个包含虚函数的类创建一个虚函数表,表中存放了该类所有虚函数的地址。
-
每个该类对象内部都有一个隐含的虚表指针,指向其所属类的虚函数表。
-
当通过基类指针或引用调用虚函数时,程序会通过对象的vptr找到vtable,再根据函数在表中的偏移量找到正确的函数地址进行调用。这个过程称为动态绑定。
-
-
-
纯虚函数与抽象类
纯虚函数是在基类中声明的没有具体实现的虚函数,语法为
virtual ReturnType Function() = 0;。包含纯虚函数的类称为抽象类,它不能实例化对象,只能作为接口使用
class Shape { // 抽象基类
public:
virtual void draw() const = 0; // 纯虚函数,定义接口
virtual ~Shape() {} // 虚析构函数至关重要
};
class Circle : public Shape {
public:
void draw() const override { // 重写虚函数
std::cout << "Drawing a circle.\n";
}
};
class Square : public Shape {
public:
void draw() const override {
std::cout << "Drawing a square.\n";
}
};
void renderShape(const Shape& shape) { // 统一用基类引用
shape.draw(); // 动态绑定!根据传入的实际对象类型调用对应的draw
}
int main() {
Circle c;
Square s;
renderShape(c); // 输出 "Drawing a circle."
renderShape(s); // 输出 "Drawing a square."
return 0;
}
1095

被折叠的 条评论
为什么被折叠?



