以下是该计划,
#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
。
上面的代码是按照抽象编写的。
如何在此代码中确保封装和多态?
答案 0 :(得分:3)
封装,抽象和多态。因为这三件事情在一起模糊,它们的含义是模糊的,而且它们在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
。它是一些外部数据。如果您通过lptr
,INITIAL_ARRAY_SIZE
不会继续使用它。所以这个循环违反了封装。您的列表没有很好地封装。大小应该是一个细节,它可以是List结构的一部分,也可以根本不需要。
您可以将大小添加到结构中并使用它来迭代。
for(int i=0; i < lptr->size; i++){
printf("Item: %d\n", lptr->a[i]);
}
但是这仍然让用户盯着struct细节。为了避免这种情况,您可以添加迭代器,用户根本不知道大小。这就像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这样的东西。他们已经考虑过大量的边缘案例和细节。