2

考虑一个简单的、可重用的库。它有一个用于当前状态的对象,以及一个用于为其提供输入的回调函数。

typedef struct Context_S Context_T;

typedef size_t (*GetBytes_T) (Context_T * ctx, uint8_t * bytes, size_t max);

struct Context_S {
    GetBytes_T byteFunc;
    void * extra;
    // more elements
};

void Init(Context_T * ctx, GetBytes_T func);
int GetNext(Context_T * ctx); // Calls callback when needs more bytes

用户可能需要一些额外的回调数据(如文件指针)。库提供了具有 1 个额外指针的函数:

void SetExtra(Context_T * ctx, void * ext); // May be called after init
void * GetExtra(Context_T const * ctx); // May be called in callback

但是,如果用户额外数据是常量,则需要他在设置数据之前丢弃常量。我可以更改函数以获取/返回 const,但是如果数据不应该是常量,这将需要在回调中进行额外的强制转换。

void SetExtra(Context_T * ctx, void const * ext);
void const * GetExtra(Context_T const * ctx);

第三种选择是在函数调用中隐藏强制转换:

void SetExtra(Context_T * ctx, void const * ext);
void * GetExtra(Context_T const * ctx);

在这种情况下隐藏演员表是个好主意吗?

我试图在可用性和类型安全之间找到平衡。但是由于我们使用的是void*指针,所以很多安全性已经消失了。

还是我忽略了一些值得考虑的事情?

4

2 回答 2

5

C 标准库也有类似的问题。众所周知,该strchr函数接受一个const char *参数并返回一个char *指向给定字符串的值。

这是 C 语言的一个缺陷:它的规定const不支持所有const可能被合理使用的方式。

遵循 C 标准的示例并非不合理:接受指向的指针,const并在将其返回给调用软件时,提供指向 non- 的指针const,如您的第三个示例中所示。

另一种选择是定义两组例程,SetExtra一个GetExtra使用 non- const,一个使用. 这些可以在运行时通过一个额外的位来强制执行,该位记录设置的上下文是还是非。但是,即使没有强制执行,它们也可能会有所帮助,因为它们可以使错误在调用代码中更加明显:阅读代码的人可以看到用于设置数据和(非)用于获取数据。(如果调用代码有些复杂并且在某些情况下使用数据而在其他情况下使用非数据,这可能无济于事,但捕获更多错误总比捕获更少错误更好。)SetExtraConstGetExtraConstconstconstconstSetExtraConstGetExtraconstconstconst

于 2013-10-07T13:37:09.257 回答
1

对于标准的“hack away”功能程序设计,它非常简单:

  • 如果函数修改了指针参数的内容,则指针不应为 const。
  • 如果函数不修改指针参数的内容,则指针应始终为 const。

但是在您的情况下,您似乎正在做一个正确的面向对象设计,其中您的代码模块是唯一知道Context_T它是什么以及它包含什么的人。(我认为第一行的 typedef 实际上在 h 文件中?)

如果是这样,您不能也不应该制作指针const。如果您使用不完整类型(“不透明”类型)实现真正的 OO 封装,尤其如此,因为在这种情况下,调用者无论如何都无法修改内容:“const 正确性”变得多余。

于 2013-10-07T14:26:45.517 回答