C语言中类型实现的抽象

时间:2018-11-26 05:52:35

标签: c header typedef

我是C编程的新手,正在尝试编写一个简单的示例。准确地说,我尝试对类型实现进行抽象,并仅使用typedef并指定可以对此类型执行的操作。我知道那时类型是不完整的,但是我打算将其完成到c文件而不是标头中。就是这样:

test.h

#ifndef _TEST_H
#define _TEST_H

typedef my_type_t;

void init(my_type_t **t);

#endif //_TEST_H

test.c

#include <stdlib.h>
#include "test.h"
                  //      implementation details
struct my_type_t{ //<---- completening my_type_t to be a struct with 1 field
    int field;
};

void init(struct my_type_t **t){ //<--- error: conflicting type for init
    *t = malloc(sizeof(struct my_type_t));
    (*t) -> field = 42;
}

像这样可能吗?我希望该实现完全隐藏有关实际类型定义的所有详细信息,只公开可以使用它进行的操作。

UPD:如果我们按如下方式重写c文件:

#include <stdlib.h>
#include "test.h"

struct internal_my_type_definition_t{
    int field;
};

void init(my_type_t **t){
    struct internal_my_type_definition_t *st = malloc(sizeof(struct internal_my_type_definition_t));
    st -> field = 42;
    *t = st;
}

这样的实现有问题吗?

2 个答案:

答案 0 :(得分:5)

在标题中,更改

typedef my_type_t;

struct my_type_t;

这是一个非常常见的模式。只需记住,您将需要一个函数来在堆上分配结构并释放它。隐藏的信息之一是结构的大小,因此API使用者实际上只能处理指向结构的指针,而不能处理结构本身。

惯用的API类似于

struct my_type_t* my_type_new(void);
void my_type_free(struct my_type_t* self);

my_type_init通常用于初始化已分配的实例,这仅在您希望在子类型的*_new函数中链接到它时才有用。

编辑:针对您的后续问题,可以想象在标头中执行以下操作:

#if !defined(MY_TYPE_NS)
#  define MY_TYPE_NS struct
#endif

typedef MY_TYPE_NS my_type_t my_type;

my_type* my_type_new(void);

/* ... */

然后,在您的* .c文件中:

#define MY_TYPE_NS union
#include "test.h"

union my_type_t {
  /* ... */
};

my_type* my_type_new(void*) {
  my_type* res = malloc(sizeof(my_type));
  res->field = 42;
  return res;
}

我发现这只是一点邪恶。我可能只是使用嵌套在该结构内部的联合,以避免代码中出现任何意外。

答案 1 :(得分:1)

您要寻找的设计模式称为“不透明类型” /“不透明指针”。

您几乎正确地拥有了它​​,只需要在标题中显式指定类型:

typedef struct my_type_t my_type_t;

这既是typedef也不是不完整类型的前向声明,它在您的.c文件中完成并且对调用者不可见。

现在,调用者可以声明指向该类型的指针,但不能声明对象。他们无法访问结构成员-我们已经实现了私有封装。您必须将函数设计为始终采用指针类型。