目录
1.数据抽象的概念
1.1访问标签的概念
1.2数据抽象的好处
2.数据封装
3.c++接口(抽象类)
3.1抽象类的定义
3.2 示例代码
3.3关键点总结
1.数据抽象的概念
数据抽象强调隐藏实现细节,只展示用户需要关注的信息。通过这种方式,用户可以专注于如何使用数据而不是如何实现这些数据。例如,操作一台电视机时,用户只需使用遥控器按钮,而不需要理解其内部电子电路如何工作。
1.1访问标签的概念
C++ 中的访问标签分为三种:
-
public:公有成员可以被任何其他代码访问。这通常用于定义类的接口,允许用户与类进行交互。
-
private:私有成员只能在类的内部访问。这是数据抽象的核心,防止外部代码直接修改对象的状态,从而避免意外错误。
-
protected:受保护的成员可以被类及其派生类访问。这在继承时尤其有用,但对外部代码仍然是不可见的。
#include <iostream>
class BankAccount {
private:
double balance; // 私有成员,隐藏实现细节
public:
// 构造函数
BankAccount(double initialBalance) : balance(initialBalance) {}
// 公有方法,用于存款
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// 公有方法,用于取款
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
// 公有方法,用于查询余额
double getBalance() const {
return balance;
}
};
int main() {
BankAccount account(100.0);
account.deposit(50.0);
account.withdraw(30.0);
std::cout << "Current Balance: " << account.getBalance() << std::endl; // 输出: Current Balance: 120
// 直接访问 balance 会导致编译错误
// std::cout << account.balance << std::endl; // 这一行将无法编译
return 0;
}
1.2数据抽象的好处
-
保护类的内部状态:
通过将数据成员设为私有,类的内部实现不会受到外部代码的干扰。这防止了因不当访问导致的状态损坏。例如,用户无法直接修改balance
,必须通过deposit()
和withdraw()
方法来操作。 -
实现的灵活性:
当实现需求变化时(如需要更改余额计算的逻辑),只需在类的内部进行修改,而不影响使用该类的外部代码。只要公共接口保持不变,外部代码无需任何修改。例如,如果需要记录交易历史,可以在deposit()
和withdraw()
方法中添加新的功能,而无需改变外部代码如何与BankAccount
交互。
2.数据封装
数据封装是面向对象编程(OOP)的核心原则之一,它涉及将数据(成员变量)和对数据进行操作的函数(成员函数)捆绑在一起。封装的主要目的包括:
安全性:通过控制对数据的访问,防止外部代码直接修改内部状态,从而减少潜在的错误和不一致性。
模块化:将数据和操作逻辑组合成一个单独的模块,便于管理和维护。
易用性:提供清晰的接口,使用户能够轻松使用类的功能,而无需了解内部实现。
3.c++接口(抽象类)
3.1抽象类的定义
抽象类是包含至少一个纯虚函数的类。纯虚函数是通过在函数声明后添加 = 0
来指定的。由于存在纯虚函数,抽象类无法被实例化,只能作为基类使用。
3.2 示例代码
#include <iostream>
// 定义一个抽象类
class Shape {
public:
// 纯虚函数,描述计算面积的接口
virtual double area() const = 0;
// 纯虚函数,描述计算周长的接口
virtual double perimeter() const = 0;
// 虚析构函数,确保派生类的析构函数被调用
virtual ~Shape() {}
};
// 具体类,继承自抽象类
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
// 实现纯虚函数
double area() const override {
return width * height;
}
double perimeter() const override {
return 2 * (width + height);
}
};
// 另一个具体类,继承自抽象类
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
// 实现纯虚函数
double area() const override {
return 3.14159 * radius * radius;
}
double perimeter() const override {
return 2 * 3.14159 * radius;
}
};
int main() {
// 无法实例化 Shape 类
// Shape shape; // 编译错误
// 使用具体类
Shape* rect = new Rectangle(10.0, 5.0);
Shape* circ = new Circle(3.0);
std::cout << "Rectangle area: " << rect->area() << std::endl;
std::cout << "Circle area: " << circ->area() << std::endl;
delete rect;
delete circ;
return 0;
}
3.3关键点总结
- 纯虚函数:通过
= 0
定义,表示该函数没有实现,必须在派生类中实现。 - 抽象类:包含至少一个纯虚函数的类,不能实例化。
- 具体类:继承自抽象类并实现所有纯虚函数的类,可以被实例化。
- 多态性:使用基类指针可以指向任何派生类对象,实现动态绑定。
这种结构允许代码遵循开闭原则,易于扩展和维护,使得新类型的形状可以轻松地添加,而不需要修改已有代码。