在C中使用未定义的结构有什么好处吗?
SQLite源代码中的示例:
/* struct sqlite3_stmt is never defined */
typedef struct sqlite3_stmt sqlite3_stmt;
对象被操纵如下:
typedef struct Vdbe Vdbe;
struct Vdbe {
/* lots of members */
};
int sqlite3_step(sqlite3_stmt *pStmt) {
Vdbe *v = (Vdbe*) pStmt;
/* do stuff with v... */
}
那么为什么不使用通常的抽象类型,在foo.c
源中私有定义的实际结构和typedef
标题中的公共foo.h
?
答案 0 :(得分:15)
这样定义是为了隐藏用户的sqlite3_stmt
的实现细节,从而避免内部状态被搞乱。请参阅Opaque pointer。
(这也迫使用户仅将类型用作指针,因为结构sqlite3_stmt
本身的实现不完整。)
编辑:VDBE(虚拟数据库引擎)只是SQLite3 API的“后端”。我认为后端是可变的,因此sqlite3_stmt*
不一定是Vdbe*
。不在API中公开Vdbe*
,因为不应公开后端细节。
答案 1 :(得分:9)
澄清:你问的是为什么SQLite会做上述事情而不是这样做:
标题文件:
typedef struct sqlite3_stmt sqlite3_stmt;
C档案:
struct sqlite3_stmt {
/* lots of members */
};
int sqlite3_step(sqlite3_stmt *pStmt) {
/* do stuff with pStmt... */
}
(这是KennyTM答案中链接的“不透明指针”模式的规范形式。)
我能想到SQLite为什么会这样做的唯一理由如下:
我推测,后端代码来自API并使用了名称Vdbe
- 这个名称可能意味着与“虚拟数据库条目”的实现相关的东西(在这里疯狂地猜测)
当创建API时,有人意识到sqlite3_step
所需的参数是Vdbe
,但这并不是一个能够向API的用户传达很多信息的名称。因此,从用户的角度来看,Vdbe
被称为sqlite3_stmt
。
然后,这里的观点是区分同一项目的两个视图:后端根据Vdbe
s(无论它们是什么)进行思考,因为这是一个在<上下文中有意义的名称EM>实施。 API讨论sqlite3_stmt
,因为这是一个在接口的上下文中有意义的名称。
编辑:正如Amarghosh所指出的,为什么不这样做才能达到同样的效果?
typedef struct Vdbe sqlite3_stmt;
KennyTM points out a good possible reason(请把他投票给我,我不想在这里掏出他的代表):VDBE只是几个可能的后端中的一个;接口使用“泛型”sqlite3_stmt
,然后将其转换为后端用于实现它的任何内容。