一、数据抽象的概念
数据抽象指的是仅向外界提供必要的信息,并隐藏其背景细节,即,在程序中表示所需的信息而不呈现细节。
数据抽象是一种编程(和设计)技术,依赖于接口与实现的分离。
让我们举一个现实生活中的例子——电视,你可以打开或关闭它,更换频道,调整音量,还可以添加外部组件如扬声器、录像机和DVD播放器,但是你并不知道它的内部细节,也就是说,你不知道它是如何通过空气或电缆接收信号,如何翻译这些信号,最后是如何将它们显示在屏幕上的。
因此,我们可以说明电视机明显地将其内部实现与其外部接口分开,你可以操作它的接口,比如电源按钮、频道切换器和音量控制,而无需了解其内部运作。
在 C++ 中,类提供了高度的数据抽象。它们向外界提供了足够的公共方法来玩转对象的功能和操纵对象数据,即状态,而无需实际了解类是如何内部实现的。
例如,你的程序可以调用 sort() 函数,而无需知道该函数实际使用的排序算法是什么。事实上,排序功能的底层实现可能在库的不同版本之间发生变化,只要接口保持不变,你的函数调用仍然可以工作。
在 C++ 中,我们使用类来定义我们自己的抽象数据类型(ADT)。你可以使用类 ostream 的对象 cout 将数据流式传输到标准输出,如下所示:
#include <iostream>
using namespace std;
int main() {
cout << "Hello C++" << endl;
return 0;
}
在这里,你不需要理解 cout 是如何在用户的屏幕上显示文本的。你只需要知道公共接口,而 ‘cout’ 的底层实现可以自由更改。
二、访问标签强制抽象
在 C++ 中,我们使用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签。
使用 public 标签定义的成员对程序的所有部分都是可访问的。类型的数据抽象视图由其公共成员定义。
使用 private 标签定义的成员对使用该类的代码是不可访问的。私有部分隐藏了类型从使用该类型的代码中实现的细节。
访问标签出现的频率没有限制。每个访问标签指定了随后成员定义的访问级别。指定的访问级别一直有效,直到遇到下一个访问标签或看到类体的闭合右花括号为止。
三、数据抽象的好处
数据抽象提供了两个重要的优势:
-
类的内部受到保护,防止无意的用户级错误,这些错误可能会破坏对象的状态。
-
类的实现在响应不断变化的需求或错误报告的过程中可以随着时间的推移而演变,而无需更改用户级别的代码。
通过仅在类的私有部分定义数据成员,类的设计者可以自由更改数据。如果实现发生变化,只需检查类代码以查看更改可能产生的影响。如果数据是公开的,那么任何直接访问旧表示形式的数据成员的函数都可能被破坏。
四、数据抽象示例
任何实现了带有公共和私有成员的类的 C++ 程序都是数据抽象的一个例子。考虑以下示例:
#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 << "总数 " << a.getTotal() << endl;
return 0;
}
当上述代码被编译和执行时,它产生如下结果:
总数 60
上面的类将数字相加,并返回总和。公共成员 addNum 和 getTotal 是对外界的接口,用户需要了解它们才能使用这个类。私有成员 total 是用户不需要知道的东西,但对于类正常运行是必要的。
五、设计策略
抽象将代码分为接口和实现。因此,在设计你的组件时,你必须使接口独立于实现,这样即使底层实现发生变化,接口也会保持不变。
在这种情况下,无论哪些程序正在使用这些接口,都不会受到影响,只需要与最新的实现进行重新编译即可。