在 C 中抽象类型实现
Abstracting over type implementation in C
我是 C 编程的新手,正在尝试编写一个简单的示例。确切地说,我试图抽象出一个类型实现并简单地使用 typedef
并指定我可以用这种类型做的操作。我知道那时类型不完整,但我打算将其完成到 c
文件中,而不是 header。在这里:
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;
}
这样实现有什么问题吗?
在你的header中,更改
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
函数中链接到它时才有用。
编辑:针对您的 follow-up 问题,您可以在 header:
中做类似的事情
#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;
}
我觉得这只是轻微的邪恶。我可能只是使用嵌套在结构内部的联合来避免代码中出现任何意外。
您要查找的设计模式称为"opaque type"/"opaque pointers"。
你几乎已经正确了,你只需要在header中明确指定类型:
typedef struct my_type_t my_type_t;
这既是一个 typedef 也是一个不完整类型的前向声明,它在您的 .c 文件中完成并且对调用者不可见。
现在调用者可以声明指向此类型的指针,但不能 objects。他们无法访问结构成员——我们已经实现了私有封装。您必须将函数设计为始终采用指针类型。
我是 C 编程的新手,正在尝试编写一个简单的示例。确切地说,我试图抽象出一个类型实现并简单地使用 typedef
并指定我可以用这种类型做的操作。我知道那时类型不完整,但我打算将其完成到 c
文件中,而不是 header。在这里:
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;
}
这样实现有什么问题吗?
在你的header中,更改
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
函数中链接到它时才有用。
编辑:针对您的 follow-up 问题,您可以在 header:
中做类似的事情#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;
}
我觉得这只是轻微的邪恶。我可能只是使用嵌套在结构内部的联合来避免代码中出现任何意外。
您要查找的设计模式称为"opaque type"/"opaque pointers"。
你几乎已经正确了,你只需要在header中明确指定类型:
typedef struct my_type_t my_type_t;
这既是一个 typedef 也是一个不完整类型的前向声明,它在您的 .c 文件中完成并且对调用者不可见。
现在调用者可以声明指向此类型的指针,但不能 objects。他们无法访问结构成员——我们已经实现了私有封装。您必须将函数设计为始终采用指针类型。