繁体   English   中英

使用const指针到const数据的C封装

[英]C encapsulation using const pointers to const data

我有一个关于在C中封装我的模块(.c / .h文件对)的问题。

人们通常使用getTime(char * time)等访问函数来访问模块的内部变量,而不提供混淆它们的能力。

现在,我正在使用常量数据的常量指针,指向内部结构来做同样的事情。 我认为这很有效,因为其他模块可以查看结构中的数据而无法改变它,并且它节省了函数调用的开销。 (这是针对嵌入式的东西,因此函数调用是“昂贵的”)。

我只是想知道这是否是有效的封装等等。我想他们可以设置一个指针,它不是常数然后他们可以搞乱数据? 一个例子:blah.h

typedef struct {
    unsigned char data;
    unsigned int intdata;
} myStruct;

extern const myStruct * const ptrConstMyStruct;

blah.c

static myStruct thisIsMyModulesData;
const myStruct * const ptrConstMyStruct = &thisIsMyModulesData;

anotherFile.c

variable = ptrConstMyStruct->data;
ptrConstMyStruct->data = variable; //compile error!

使用不完整的类型并只是在头文件中声明结构是首选方法。

就封装而言,Constness更像是一种声明特定函数不会改变给定对象的方法。 在你的解决方案中,你必须抛弃似乎反直觉的常量。 或者你不使用const指针作为变异函数的参数吗?

此外,封装是关于隐藏实现和细节,如果您公开作为实现的结构, 无法完成。

编辑以解释前向声明:

MyCode.h:

struct my_struct;

mycode.c中:

struct my_struct { .... };

以上意味着MyCode的用户将能够使用指向my_struct的指针,但不能检查其成员。

封装允许您更改内部机制,并阻止用户更改内部数据。 一种方法是使用getter和setter函数,它们都可以封装并允许你执行const事务。

您的问题中有一些陈述可能表明问题的真正根源。

我认为这很有效,因为其他模块可以查看数据......

如果其他模块可以查看数据,则不会对其进行封装。 如果其他模块需要查看(原始)数据,那么为什么要尝试将其设为私有? 这暗示了程序设计中的一些基本缺陷。

EXTERN

您永远不需要在C语言中使用全局变量(可能除MCU硬件外设寄存器外)。 我在过去10年中没有使用过全局变量,而且我几乎只使用嵌入式实时系统。 同样,这表明程序设计中出现了问题。

...它节省了函数调用的开销。 (这是针对嵌入式的东西,因此函数调用是“昂贵的”)。

不,不是。 C语言支持函数内联13年,即使你有一个旧的C90编译器,我敢打赌它有一个内联选项, #pragma inline或者其他一些。 我还没有看到缺少内联的嵌入式编译器。 最重要的是,如果只启用优化,即使是旧编译器也能够在没有程序员任何明确提示的情况下执行内联。

此外,函数调用开销是程序中的瓶颈,您通过示波器进行基准测试/测量找到了这个瓶颈吗? 如果没有,那么为什么要使用全局变量和暴露私有数据的奇怪指针来混淆代码呢? 那是不成熟的优化。

它不是封装,除非您能够在结构的“用户”读取代码和数据所在的位置之间注入代码。 这样的垫片允许您在不改变结构的外部使用的情况下改变结构的内部。

虽然您的解决方案是对通常在C中完成事务的方式的改进,但为了封装它,您需要能够将字段从单个值更改为构造值,而无需更改任何“外部”代码来证明它是真的封装了。

在C中,这通常通过将数据隐藏在void指针之后,或者在封装代码的外部部分上使用声明的(但未定义的)结构来执行。

blah.h

struct myStruct_t;
typedef struct myStruct_t myStruct;

extern myStruct * const ptrConstMyStruct;

// if you want a constructor, you need to declare the
// method here, because only blah.c will know the struct
// size with this solution.
myStruct * new_myStruct();

// methods
myStruct_setData(myStruct* obj, char c);
char myStruct_getData(myStruct* obj);

blah.c

#include "blah.h"

struct myStruct_t {
    unsigned char data;
    unsigned int intdata;
};

static myStruct thisIsMyModulesData;

// no need to make the struct const, just the pointer
// otherwise, we would have to break the const just to change
// data, which now will only be done within this file.
myStruct * const ptrConstMyStruct = &thisIsMyModulesData;

anotherFile.c

#include "blah.h"
// anotherFile "knows" of a struct called myStruct, but
// it doesn't know what it contains, or even it's size.

// this is no longer possible
// now "data" is encapsulated, and can be changed
// by reimplementing `myStruct_getData(...)`
// (as long as we don't change the method signature).
variable = ptrConstMyStruct->data;

// this is the "new" way
variable = myStruct_getData(ptrConstmyStruct);

// with the old, compiler error because of 
// assigning a value to a const value.
ptrConstMyStruct->data = variable; //compile error!
                       ^
              (error occurs here)

// with the new, compiler error because of
// dereferencing a pointer to a unknown / incomplete type.
ptrConstMyStruct->data = variable; // compile error!
                ^
        (error occurs here)

正如您所看到的,错误的位置使得封装与否之间存在差异。 如果检查赋值时的错误而不是取消引用,则无法使用ptrConstMyStruct->data更改指针和数据之间的关系。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM