【问题标题】:Object orient program in C - ListC中的面向对象程序 - 列表
【发布时间】:2017-03-24 21:48:37
【问题描述】:

下面是程序,

#include<stddef.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

#define INITIAL_ARRAY_SIZE 10

typedef struct{
  int *a;
  int lastItem; //Location of lastest element stored in array
  int size;     //Size of array
}List;



void newList(List **lptr, int size){
  *lptr = malloc(sizeof(List));
  (*lptr)->a = calloc(size, sizeof(int));
  (*lptr)->lastItem = -1;
  (*lpr)->size =0;

}

List* insertItem(List *lptr, int newItem){

  if(lptr->lastItem + 1 == lptr->size){

    List *newLptr = NULL;
    newList(&newLptr, 2*lptr->size);
    memcpy(newLptr->a, lptr->a, (lptr->lastItem)+1);
    newLptr->lastItem = lptr->lastItem;
    newLptr->size = 2*lptr->size;
    newLptr->a[++(newLptr->lastItem)] = newItem;

    free(lptr);
    return newLptr;

}
  lptr->a[++(lptr->lastItem)] = newItem;
  return lptr;
}

int main(void){
  List *lptr = NULL;
  newList(&lptr, INITIAL_ARRAY_SIZE);

  lptr = insertItem(lptr, 6);

  for(int i=0; i < INITIAL_ARRAY_SIZE;i++){
    printf("Item: %d\n", lptr->a[i]);
  }
  printf("last item value: %d", lptr->lastItem);
}

为使用 C 数组实现 List 而编写。

上面的代码是按照抽象写的。

在这段代码中如何保证封装性和多态性?

【问题讨论】:

  • 多态性?我在这里没有看到...
  • 您充其量只是在使用抽象原则。不过那里没有封装或多态性。
  • 与您的问题无关,但您的 insertItem 函数存在缺陷。首先,当您对指针执行sizeof 时,您会得到 pointer 的大小,而不是它所指向的大小。其次,它不会建立。第三,您创建了一个全新的列表结构,当函数返回时您将“忘记”它。关于realloc 函数,您可能想阅读的最后一件事。该功能还存在一些其他问题。
  • 老实说,下面的答案比我能提供的任何答案都好。我也会仔细研究它。

标签: c oop polymorphism abstraction


【解决方案1】:

封装、抽象和多态。因为这三件事混淆在一起,它们的含义也很模糊,而且它们在 C 中有点难以实现,所以我在此答案中是如何定义它们的。

Encapsulation 限制或在 C 的情况下不鼓励了解底层事物如何工作。理想情况下,数据和方法捆绑在一起。

Abstraction 向用户隐藏了复杂性,通常通过适用于多种场景的良好定义的界面。

Polymorphism 允许同一接口用于多种类型。

他们建立在彼此之上。非常普遍地,封装允许抽象允许多态。


首先,让我们从封装违规开始。

  for(int i=0; i < INITIAL_ARRAY_SIZE;i++){
    printf("Item: %d\n", lptr->a[i]);
  }

INITIAL_ARRAY_SIZE 不是lptr 的一部分。这是一些外部数据。如果你绕过lptrINITIAL_ARRAY_SIZE 将不会接受。所以这个循环违反了封装。您的列表没有很好地封装。大小应该是 List 结构的一部分或根本不需要的细节。

您可以将大小添加到结构并使用它进行迭代。

  for(int i=0; i < lptr->size; i++){
    printf("Item: %d\n", lptr->a[i]);
  }

但这仍然让用户查看结构细节。为避免这种情况,您可以添加一个迭代器,而用户根本不知道大小。这类似于the C++ vector interface,但更尴尬,因为 C 缺少方法调用。

ListIter iter;
int *value;

/* Associate the iterator with the List */
ListIterInit(&iter, lptr);

/* ListIterNext returns a pointer so it can use NULL to stop */
/* Otherwise you can't store 0 */
while( value = ListIterNext(&iter) ) {
    printf("Item: %d\n", *value);
}

现在结构可以完全控制事物的迭代方式和存储方式。

这个迭代器接口的灵感来自Gnome Lib Hash Tables


这个迭代器接口也提供了抽象。我们删除了有关该结构的详细信息。现在,您只需迭代即可。您不需要知道数据是如何存储的,或者有多少,甚至根本不需要知道它是否被存储。众所周知,它可以即时生成。这就是iterator pattern 的美妙之处。

...除了我们仍然需要知道类型。这可以通过两种方式解决。首先是通过告诉列表每个元素有多大。与其修改你的,不如看看how Gnome Lib does it with their arrays

GArray *garray = g_array_new (FALSE, FALSE, sizeof (gint));
for (i = 0; i < 10000; i++) {
  g_array_append_val (garray, i);
}

数组被告知要存储sizeof(gint) 的东西,它会记住这些东西。然后所有其他数组操作都封装在一个函数中。甚至取出一个元素也是封装的。

gint g_array_index(garray, gint, 5);

这是通过一个为您进行类型转换的聪明宏来完成的。

第二种选择是将所有数据存储为指针。我会把它作为练习留给你看Gnome Lib's pointer arrays


你会看那个吗?我们有多态性。单个数组结构现在可以处理所有类型的数据。

这在 C 中并不是特别容易做到的。它涉及到一些宏的杂耍。看看 Gnome Lib 之类的东西,从概念上了解它是如何完成的,这很好。也许尝试自己做以练习和理解。

但对于生产,只需使用 Gnome Lib 之类的东西。他们已经考虑过大量的边缘案例和小细节。

【讨论】:

    猜你喜欢
    • 2011-04-16
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多