您的位置:首页 > 其它

对数据类型封装和数据抽象的简单理解

2014-11-15 23:04 337 查看
  请特别关注程序设计技术,而不是各种语言特征。

                        --《C++程序设计语言》 Bjarne Stroustrup

  本文是《C++程序设计语言》(Bjarne Stroustrup )的第二章的读书笔记,例子来源于这本书的第二章。
  在程序设计之中,我们倾向于将数据结构(也可以说是数据类型)以及一组对其操作的相关过程组织在一起,在逻辑上可以称将其为模块。此时程序分为一些模块,模块包括一组对数据的操作,数据隐藏于模块之中。以下以栈的设计为例,使用C和C++进行设计,简单理解模块化设计中的数据封装和数据抽象。
  对于C 语言我们可以对栈这一数据类型进行如下简单的设计:

typedef struct Stack
{
  int elem[MAX_SIZE];
  int top;
} Stack;
Stack* createStack();
void destroyStack(Stack*);
void push(Stack*,int);
int pop(Stack*);


  由于struct结构可以自由的有外界进行访问,因此可能对数据进行破坏。我们可以引入类似于win32编程使用到的句柄的设计。即:

typedef void* HStack;
HStack createStack();
void destroyStack(HStack hStack);
void push(HStack hStatck,int)
{
struct Stack* stack = (struct Stack*)hStack;
........
}
int pop(HStack hStack)
{
struct Stack* stack = (struct Stack*)hStack;
........
}


  只有在实现该数据类型相关的操作,才知道Stack的内在结构,可以进行强制到转换。因此该模块的用户无需知道struct Stack的结构,改变struct Stack结构也不会影响到API用户的使用。

客户端代码:

void f()
{
HStack h= createStack();
push(h,2);
int i = pop(h);
destroyStack(h);
}


  因此C语言在进行类似的数据类型封装时,通常提供的一组操作中通常包含了初始化和回收的操作,通常需要用户有意识的进行调用。这让这一数据类型有点“伪类型”的感觉,C语言的特征无法让封装的数据类型进行自动的初始化和销毁。

  C++提供类这一用户自定义类型对数据类型封装进行支持。在进行程序设计时,通过确定需要哪些类型,为每个类型提供完整的操作。对栈的定义如下:

class Stack
{
public:
Stack(int max_size);
~Stack();
void push(int);
int push();
private:
int* m_elem;
int m_top;
int m_max_size;
};


  构造函数Stack(int) 在建立这个类的对象时被调用,处理初始化问题。如果该类的一个对象出了其作用域,进行某些清理的时候,通过调用其析构函数。

  像上面的Stack这种类型的定义,我们可以称为就具体类型,涉及到具体的实现。在类型不常改变,或者类型用于局部变量的情况下,这种设计方法足够解决问题。在一些情况下我们希望有抽象类型。抽象类型可以将用户与实现细节隔离,得到更好的灵活性。此时用户面向抽象类型编程,而不是面向具体类型编程。 C++可以通过类的继承和抽象类实现这一方式。如:

class Stack
{
public:
virtual void push(int) = 0;
virtual int pop(int) = 0;
};

// 使用数组实现栈
class ArrayStack : public Stack {};

// 使用链表实现栈
class ListStack : public Stack{};

// 用户面向抽象类编程:
void f(Stack& s)
{
s.push(2);
int i = s.pop();
}


  通过定义Stack这一抽象类(在C++中可以理解为具有纯虚成员函数的类。 如:virtual void push(int) = 0;),为这一抽象类提供不同实现获得灵活性,用户面向这一抽象类编程(在C++中,通常是该类型对象的引用或者指针,才能实现多态)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐