我会避免使用malloc来初始化一个结构,而我正在寻找使用oo风格设计C软件的最佳实践(如果可能的话)。
仅限C99,而非C ++
第一个问题,使用类似对象的结构时最好的是什么?是否指定了它的指针?
这些是我的测试(所有工作都使用gcc编译器):
案例1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct sItem{
int n;
char* text;
} oItem, *Item;
int main(int argc, char** argv) {
Item i1=(&(oItem){.n=1, .text="A"});
Item i2=(&(oItem){.n=100, .text="ABC"});
printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 1, "A", 8
printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 1, "ABC", 8
return (EXIT_SUCCESS);
}
这有效,但我认为不应该因为文本未初始化为包含字符串。 这是一段无效的代码吗?
案例2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct sItem{
int n;
char text[5];
} oItem, *Item;
int main(int argc, char** argv) {
Item i1=(&(oItem){.n=1, .text="A"});
Item i2=(&(oItem){.n=100, .text="ABC"});
printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 1, "A", 12
printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 1, "ABC", 12
return (EXIT_SUCCESS);
}
这有效,我认为这是正确的,是吗?
案例3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Item_new(i, n, s) (&(oItem){0});Item_ctor(i, n, s);
#define Item_neww(i, x, s) (&(oItem){\
.n=x,\
.text=s\
})
typedef struct sItem{
int n;
char text[5];
} oItem, *Item;
void Item_ctor(Item i, int n, char* text){
i->n=n;
strcpy(i->text, text);
}
int main(int argc, char** argv) {
Item i1=Item_new(i1, 10, "ABC");
Item i2=Item_neww(i2, 10, "ABC");
printf("%d, %s, %d\n", i1->n, i1->text, sizeof(*i1)); // 10, "ABC", 12
printf("%d, %s, %d\n", i2->n, i2->text, sizeof(*i2)); // 10, "ABC", 12
return (EXIT_SUCCESS);
}
我觉得这很好,但隐藏了代码,也许可能有害,你怎么看? 我的情况3,最好的选择是什么:宏或构造函数?
答案 0 :(得分:2)
不要做3,包含不受保护的;
的宏让我非常紧张。
相反,我会用以下
替换你的“新”和“ctor”#define Item_new(i, n, s) Item_ctor(&(oItem){0}, n, s)
Item Item_ctor(Item i, int n, char* text){
if (i) {
i->n=n;
strncpy(i->text, text, 4);
}
return i;
}
这并没有打破用户对Item_new
的期望:一个真实的
像宏一样返回一个值。
并且ctor应该进行必要的检查并且永远不会覆盖内存,i->text[4]
将永远是0
。 (最好是使用符号常量而不是5
,并将其用于strncpy
调用。)
答案 1 :(得分:1)
案例3是我看到的,并且会推荐。在构造函数中包装代码非常好 - 为什么隐藏代码会有问题?实际上,这是一个功能 - 将接口隐藏在实现之外。另外,不要使用宏 - 对于宏来说这是一个太复杂的任务,然而,宏通常是邪恶的。
方法1和2非常难看(事实证明,它们也是UB)并且在我看来是不可读的。此外,情况1不是const正确的,或者使用情况2(好吧,不要因为它调用未定义的行为而使用它)或者将“text”声明为const char *
。