【问题标题】:Hiding Struct Members隐藏结构成员
【发布时间】:2019-10-28 18:04:18
【问题描述】:

我知道以前可能有人问过这个问题,但我想采取我的方法并获得意见或可能是更好的方法。 我有三个文件 a.h a.cmain.c 关于结构的函数原型将在 a.h 而实现将在 a.c 并从 main.c 调用结构将很简单,它看起来像这个

struct ctx{
    int x;
};

我希望a.c 能够操纵结构的内容,但阻止 main 知道里面有什么。所以我想把结构定义放在a.c而不是a.h中,并将struct ctx;作为原型放在a.h中 这可以工作,但是 ctx 不能再在 main.c 的堆栈上分配,因为编译器不知道要分配的大小。 所以这引出了我的第一个问题:有没有办法在不知道结构定义的情况下将结构分配到堆栈本地。

所以我假设如果它在堆栈上是不可能的,那么也许我可以通过创建一个返回指针的简单 init 函数来将它传递到堆上。这确实有效,但是否会使过程过于复杂?

a.h

#ifndef a_h
#define a_h

#include <stdio.h>
#include <stdlib.h>
struct ctx;
int incctx(struct ctx* c);
struct ctx* initctx(void);
void destroyctx(struct ctx* c);
#endif /* a_h */

a.c

#include "a.h"
struct ctx{
    int x;
};
int incctx(struct ctx* c){
    return ++c->x;
}
struct ctx* initctx(){
    struct ctx* c =  malloc(sizeof(struct ctx));
    c->x = 0;
    return c;
}
void destroyctx(struct ctx* c){
    free(c);
}

main.c

#include "a.h"

int main(){
    struct ctx* c = initctx();
    printf("%d\n",incctx(c));
    printf("%d\n",incctx(c));
    printf("%d\n",incctx(c));
    destroyctx(c);
    return 0;
}

这种设计解决了一些缺点。 1:如果我想让结构的一部分可见而不是整个东西怎么办? 2:如果我希望结构定义可用于其他文件,比如b.hb.c,我是否必须重新定义结构?你们中有人有更清洁的设计吗?我知道有些人说你可以在结构中放置一个 void* 而不是特定类型,然后给它们贴上任意名称,但我不认为这是一个可行的解决方案。

【问题讨论】:

  • 这样的数据类型被称为opaque data types 并且很常见。有关一个非常常见的示例,请参见标准 C FILE 结构。
  • 我可以看到 FILE 结构的内容,我可以将它声明为堆栈本地。
  • 不要在main中包含a.h;只需定义void *ctx;
  • 你不应该像FILE file;那样创建FILE类型的非指针实例。如果您可以这样做或查看成员,那么这可能是您的编译器的特定于调试的扩展。
  • 是的,奇怪的是,在我的发布版本中,这段代码编译了`FILE f; fclose(&f);`

标签: c struct private


【解决方案1】:

对于可见性问题,您可以以类似继承的方式使用两个结构。

首先,您拥有在头文件中定义的公共结构,并且您的 API 处理指向的指针:

struct ctx
{
    // Public data
};

然后在源文件中创建一个私有结构,其中公共结构是第一个成员:

struct private_ctx
{
    struct ctx ctx;  // The public part of the structure

    // Followed by private data
};

在 API 内部,您使用 private_ctx 结构,而使用您的 API 的代码将仅使用公共 ctx 结构。

像这样的嵌套结构类似于继承,private_ctx 结构是一个 ctx 结构。您可以创建一个private_ctx 结构并返回一个指向它的指针,该指针适当地转换为ctx 结构。

这是一个关于如何创建结构的示例:

struct ctx *create_struct(void)
{
    // Allocate the private structure, which contains the public structure
    struct private_ctx *data = = malloc(sizeof *data);

    // Return the public part of the structure
    return (struct ctx *) data;
}

通过反向转换使用私有数据同样容易:

void use_data(struct ctx *data)
{
    struct private_ctx *private_data = (struct private_ctx *) data;

    // Here the private data can be used...
}

【讨论】:

  • 因此,如果它作为参数传递给私有结构,我将自己计算来自公共 ctx 结构的偏移量。喜欢c+=sizeof(struct ctx)
  • @Nina 不需要。更新了我的答案。
  • 这个解决方案的问题是有人要写struct ctx *myCtx = malloc( sizeof( *myCtx ) );,将structpublic版本传递为useData( myCtx );,然后事情就会爆炸.
【解决方案2】:

抽象隐藏可以通过让你的模块将指向结构的指针分发给 main 并让你的模块对其执行所有操作来实现。那么 main 只知道 ctx 是一些 void 数据类型(指针)就足够了,例如

// main.c

void *initctx(void);
int incctx(void *c);

int main(void)
{
    void *ctx= initctx();
    int i= incctx(ctx);
    //....
}

【讨论】:

  • 所以将实现保留在 C 文件中并让我的所有参数/返回都为 void*?
  • 是的。也就是说,据我所知,这是最干净的方式。
  • 如果我希望结构的一个成员公开怎么办。像 void* 数据成员,但其他所有内容都是私有的?用户知道结构的类型并且可以在回调期间获取他们的 void* 数据成员。我认为这就是@Some 程序员老兄解决方案更可行的地方。
  • 不要公开。使用获取和设置特定数据元素的所谓“getter”和“setter”,例如int ctxGetx(void *ctx);void ctxSetx(void *ctx, int x);
  • 啊,我明白了,这是一种有趣的方式。谢谢你的建议!
【解决方案3】:

你想把整个结构隐藏在 a.c 中

分配结构时,不要返回指向它的指针,而是返回句柄,它可能是你在 a.c 中维护的结构数组的索引

如果您想将结构的任何部分暴露给外部,请提供 getSomething(int handle) 或 setSomething(int handle) 等函数。

open(),返回一个 int 使用这种方法,而不是 fopen(),它返回一个 FILE *

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多