对于使用结构定义的C用户类型,是否有某种方法可以使用某种默认构造函数(如C++版本)?
我已经有一个像快速初始化程序(就像那个一样)的宏,pthread_mutex但我想知道你是否可以在声明中填充结构的某些(或所有)字段.
例如,通过这个pthread_mutex例子,我想
pthread_mutex_t my_mutex;
Run Code Online (Sandbox Code Playgroud)
具有相同的效果
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
Run Code Online (Sandbox Code Playgroud)
Tim*_*Tim 38
您可以创建带有指向结构的指针的初始化函数.这是常见的做法.
也是创建结构并初始化它的函数(如工厂) - 所以从来没有时间结构在"客户端"代码中"未初始化".当然 - 假设人们遵循惯例并使用"构造函数"/工厂......
可怕的伪代码,没有错误检查malloc或免费
somestruct* somestruct_factory(/* per haps some initializer agrs? */)
{
malloc some stuff
fill in some stuff
return pointer to malloced stuff
}
void somestruct_destructor(somestruct*)
{
do cleanup stuff and also free pointer
free(somestruct);
}
Run Code Online (Sandbox Code Playgroud)
有人可能会出现并解释一些早期C++预处理器/编译器如何在C中完成所有这些工作.
Ree*_*eed 21
在这种情况下,C++与C不同,它没有"类".但是,C(与许多其他语言一样)仍然可以用于面向对象的编程.在这种情况下,构造函数可以是初始化结构的函数.这与构造函数相同(只有不同的语法).另一个区别是你必须使用malloc()(或某些变体)分配对象.在C++中,您可以使用'new'运算符.
例如C++代码:
class A {
public:
A() { a = 0; }
int a;
};
int main()
{
A b;
A *c = new A;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
等效C代码:
struct A {
int a;
};
void init_A_types(struct A* t)
{
t->a = 0;
}
int main()
{
struct A b;
struct A *c = malloc(sizeof(struct A));
init_A_types(&b);
init_A_types(c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
函数'init_A_types'在C++中作为构造函数运行.
pli*_*nth 11
让我们来谈谈在过去被认为是最佳实践的完整工程解决方案.
结构的问题是一切都是公共的,所以没有数据隐藏.
我们可以解决这个问题
您创建两个头文件.一个是代码客户端使用的"公共"头文件.它包含这样的定义:
typedef struct t_ProcessStruct *t_ProcessHandle;
extern t_ProcessHandle NewProcess();
extern void DisposeProcess(t_ProcessHandle handle);
typedef struct t_PermissionsStruct *t_PermissionsHandle;
extern t_PermissionsHandle NewPermissions();
extern void DisposePermissions(t_PermissionsHandle handle);
extern void SetProcessPermissions(t_ProcessHandle proc, t_PermissionsHandle perm);
Run Code Online (Sandbox Code Playgroud)
然后你创建一个私有头文件,其中包含如下定义:
typedef void (*fDisposeFunction)(void *memoryBlock);
typedef struct {
fDisposeFunction _dispose;
} t_DisposableStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PID _pid;
/* etc */
} t_ProcessStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PERM_FLAGS _flags;
/* etc */
} t_PermissionsStruct;
Run Code Online (Sandbox Code Playgroud)
然后在您的实现中,您可以执行以下操作:
static void DisposeMallocBlock(void *process) { if (process) free(process); }
static void *NewMallocedDisposer(size_t size)
{
assert(size > sizeof(t_DisposableStruct);
t_DisposableStruct *disp = (t_DisposableStruct *)malloc(size);
if (disp) {
disp->_dispose = DisposeMallocBlock;
}
return disp;
}
static void DisposeUsingDisposer(t_DisposableStruct *ds)
{
assert(ds);
ds->_dispose(ds);
}
t_ProcessHandle NewProcess()
{
t_ProcessHandle proc = (t_ProcessHandle)NewMallocedDisposer(sizeof(t_ProcessStruct));
if (proc) {
proc->PID = NextPID(); /* etc */
}
return proc;
}
void DisposeProcess(t_ProcessHandle proc)
{
DisposeUsingDisposer(&(proc->_disposer));
}
Run Code Online (Sandbox Code Playgroud)
会发生什么是您在公共头文件中为结构做出前向声明.现在你的结构是不透明的,这意味着客户不能与它们混在一起.然后,在完整声明中,在每个结构的开头都包含一个析构函数,您可以调用它.您可以为每个人使用相同的malloc分配器具有相同的dispose功能.您为要公开的元素创建公共set/get函数.
突然间,你的代码更加清晰.您只能从分配器或调用分配器的函数获取结构,这意味着您可以瓶颈初始化.您构建析构函数以便可以销毁对象.你去吧.顺便说一下,比t_DisposableStruct更好的名字可能是t_vTableStruct,因为它就是这样.您现在可以通过使用vTableStruct来构建虚拟继承,该vTableStruct是所有函数指针.你也可以用纯粹的oo语言(通常)做一些你不能做的事情,比如在运行中更改vtable的select元素.
最重要的一点是,有是用于制造结构安全和initializable工程模式.
您可以编写一个返回 C 结构的函数:
struct file create_file(int i, float f) {
struct file obj = { i, f };
// other code here...
return obj;
}
Run Code Online (Sandbox Code Playgroud)
如果你想知道你是否可以在 C 中拥有“正常”的成员函数。嗯,在某种程度上你可以。我更喜欢 object-as-first-argument 风格。您将指向结构的指针作为第一个参数传递。这样,您可以拥有多个函数,定义对象的接口:
int file_get_integer(struct file *self) { return self->i; }
float file_get_float(struct file *self) { return self->f; }
Run Code Online (Sandbox Code Playgroud)
如果你用那种风格来写,你最后得到的是一个抽象的数据类型。我见过有人通过将函数指针指向他们的结构然后执行以下操作来模拟 C++ 中使用的成员函数调用语法:
obj.get_integer(&obj);
Run Code Online (Sandbox Code Playgroud)
linux 内核使用它来定义文件系统驱动程序的接口。这是一种人们可能喜欢也可能不喜欢的写作风格。我不太喜欢它,因为我一直使用结构的成员来处理数据,而不是模拟流行的面向对象语言中的成员函数调用。