C++ 中的数据抽象
数据抽象是指仅向外部世界提供必要信息并隐藏其背景细节,即在程序中表示所需信息而不呈现细节。
数据抽象是一种编程(和设计)技术,它依赖于接口与实现的分离。
让我们举一个现实生活中的例子:电视机,你可以打开和关闭它、切换频道、调节音量,并添加外部组件如扬声器、VCR 和 DVD 播放器,但你不知道其内部细节,即你不知道它如何通过空中或电缆接收信号、如何解码这些信号,并最终在屏幕上显示它们。
因此,我们可以说电视机清楚地将内部实现与其外部接口分离,你可以操作其接口如电源按钮、频道切换器和音量控制,而无需了解其内部实现。
在 C++ 中,class 提供了高度的 data abstraction。它们向外部世界提供足够的 public 方法来使用对象的功能并操作对象数据,即状态,而无需实际了解 class 的内部实现方式。
例如,你的程序可以调用 sort() function,而无需知道该 function 实际使用什么算法来排序给定的值。事实上,排序功能的底层实现可能在库的发布版本之间发生变化,只要接口保持不变,你的 function 调用仍然有效。
在 C++ 中,我们使用 classes 来定义自己的抽象数据类型 (ADT)。你可以使用 ostream class 的 cout 对象将数据流式传输到标准输出,如下所示 −
#include <iostream>
using namespace std;
int main() {
cout << "Hello C++" <<endl;
return 0;
}
在这里,你无需了解 cout 如何在用户屏幕上显示文本。你只需知道 public 接口,而 cout 的底层实现可以自由更改。
访问标签强制执行抽象
在 C++ 中,我们使用访问标签来定义 class 的抽象接口。一个 class 可以包含零个或多个访问标签 −
使用 public 标签定义的成员对程序的所有部分都可访问。该类型的 data-abstraction 视图由其 public 成员定义。
使用 private 标签定义的成员对使用该 class 的代码不可访问。private 部分将实现隐藏起来,不让使用该类型的代码访问。
对访问标签出现的频率没有限制。每个访问标签指定后续成员定义的访问级别。指定的访问级别一直有效,直到遇到下一个访问标签或 class 体的右大括号。
数据抽象的好处
数据抽象提供了两个重要优势 −
class 内部免受用户级意外错误的影响,这些错误可能破坏对象的状态。
class 实现可以随着需求变化或 bug 报告而随时间演进,而无需更改用户级代码。
通过仅在 class 的 private 部分定义数据成员,class 作者可以自由更改数据。如果实现发生变化,只需检查 class 代码即可了解更改可能带来的影响。如果数据是 public 的,那么任何直接访问旧表示形式数据成员的 function 都可能被破坏。
数据抽象示例
任何在 C++ 程序中实现具有 public 和 private 成员的 class 的例子,都是数据抽象的示例。请考虑以下示例 −
#include <iostream>
using namespace std;
class Adder {
public:
// 构造函数
Adder(int i = 0) {
total = i;
}
// 对外界接口
void addNum(int number) {
total += number;
}
// 对外界接口
int getTotal() {
return total;
};
private:
// 对外界隐藏的数据
int total;
};
int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
当上述代码编译并执行时,会产生以下结果 −
Total 60
上述 class 将数字相加并返回总和。public 成员 - addNum 和 getTotal 是对外界的接口,用户需要了解它们才能使用该 class。私有的成员 total 是用户不需要知道的,但 class 正常运行所必需的。
设计策略
抽象将代码分离为接口和实现。因此,在设计组件时,必须保持接口独立于实现,这样如果更改底层实现,接口仍将保持不变。
在这种情况下,使用这些接口的任何程序都不会受到影响,只需使用最新的实现重新编译即可。