对于使用结构定义的C用户类型,是否有某种方法可以使用某种默认构造函数(如C ++版本)?
我已经有一个像快速初始化程序一样工作的宏(就像pthread_mutex
那样),但我想知道你是否可以在声明中填充结构的某些(或所有)字段。
例如,使用pthread_mutex
示例,我想
pthread_mutex_t my_mutex;
与
具有相同的效果pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
答案 0 :(得分:36)
您可以创建带有指向结构的指针的初始化函数。这是常见的做法。
也是创建结构并初始化它的函数(比如工厂) - 所以在“客户端”代码中从来没有时间结构是“未初始化”的。当然 - 假设人们遵循惯例并使用“构造函数”/工厂......
可怕的伪代码,没有错误检查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);
}
有人可能会出现并解释一些早期C ++预处理器/编译器如何在C中完成所有工作。
答案 1 :(得分:19)
e.g。 C ++代码:
class A {
public:
A() { a = 0; }
int a;
};
int main()
{
A b;
A *c = new A;
return 0;
}
等效的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;
}
函数'init_A_types'在C ++中作为构造函数运行。
答案 2 :(得分: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);
然后您创建一个包含以下定义的私有头文件:
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;
然后在您的实现中,您可以执行以下操作:
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));
}
您可以在公共头文件中为结构进行前向声明。现在你的结构是不透明的,这意味着客户不能与它们混在一起。然后,在完整声明中,在每个结构的开头都包含一个析构函数,您可以调用它。您可以为每个人使用相同的malloc分配器具有相同的dispose功能。您为要公开的元素创建公共set / get函数。
突然间,你的代码更加清晰。您只能从分配器或调用分配器的函数获取结构,这意味着您可以瓶颈初始化。您构建析构函数以便可以销毁对象。你去吧。顺便说一下,比t_DisposableStruct更好的名字可能是t_vTableStruct,因为它就是这样。您现在可以通过使用vTableStruct来构建虚拟继承,该vTableStruct是所有函数指针。你也可以用纯粹的oo语言(通常)做一些你不能做的事情,比如在运行中更改vtable的select元素。
重要的一点是,是一种工程模式,可以使结构安全和可初始化。
答案 3 :(得分:8)
不,不是直接的。你最接近的是编写一个分配实例的函数并填充一些字段。
答案 4 :(得分:4)
您可以编写一个返回C结构的函数:
struct file create_file(int i, float f) {
struct file obj = { i, f };
// other code here...
return obj;
}
如果你想知道你是否可以在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; }
如果你用那种风格写作,你最后得到的是一种抽象的数据类型。我已经看到有人模仿C ++中使用的成员函数调用语法,方法是将函数指针放入其结构中然后执行:
obj.get_integer(&obj);
Linux内核使用它来定义文件系统驱动程序的接口。这是一种人们喜欢或可能不喜欢的写作风格。我不太喜欢它,因为我继续使用结构的成员来获取数据,而不是模仿成员函数调用看起来像流行的面向对象语言。
答案 5 :(得分:3)
基本上,C ++创建包含方法地址的指针列表。此列表称为类定义(类def中有更多数据,但我们现在忽略它。)
在纯C中拥有“类”的常见模式是定义“struct class”。结构的一个字段是工厂函数,它返回类的“实例”。我建议使用宏来隐藏演员阵容:
typedef struct __class * class;
typedef void (*ctor_ptr)(class);
struct class {
char * name;
ctor_ptr ctor;
... destructor and other stuff ...
}
#define NEW(clz) ((struct something *)(((struct class *)clz)->ctor(clz)))
现在,您可以通过为每个类创建类型为“struct class”的结构来定义您拥有的类,然后调用存储在其中的构造函数。对于析构函数等也是如此。
如果您需要实例的方法,必须将它们放入类结构中并在实例结构中保留指向该类的指针:
#define NEW_SOMETHING() ((struct something *)NEW(&something_definition))
#define METHOD(inst, arg) ((struct something_class *)(((struct something *)inst)->clz)->method(inst, arg))
NEW_SOMETHING
将使用存储在结构something_definition
中的类定义创建“某事”的新实例。 METHOD将在此实例上调用“方法”。请注意,对于实际代码,您需要检查inst
实际上是something
的实例(比较类指针,类似于此)。
继承有点棘手,留给读者练习。
请注意,您可以使用纯C在C ++中执行所有操作.C ++编译器不会用其他东西神奇地替换您的CPU。这需要更多代码才能完成。
如果您想查看示例,请查看glib(Gtk +项目的一部分)。
答案 6 :(得分:2)
假设您想在C中执行此操作,那么您的问题不是关于C ++中的结构:
我通常做的是我只创建一个函数init_whatever,它接受指向struct(或其他变量)的指针,并将其设置为我想要的值。
如果它是一个全局变量(初始化为零),你可以通过在其中包含“初始化”标志来模拟无参数构造函数,然后让其他函数检查该标志,并在标志为零时初始化结构。不过,我完全不确定这是个好主意。
而且,正如其他人所说,你也可以用宏做一些可怕的事情......
答案 7 :(得分:2)
这是围绕malloc()
,memcpy()
和C99复合文字的一点宏观魔法:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define new(TYPE, ...) memdup(&(TYPE){ __VA_ARGS__ }, sizeof(TYPE))
void * memdup(const void * obj, size_t size)
{
void * copy = malloc(size);
return copy ? memcpy(copy, obj, size) : NULL;
}
struct point
{
int x;
int y;
};
int main()
{
int * i = new(int, 1);
struct point * p = new(struct point, 2, 3);
printf("%i %i %i", *i, p->x, p->y);
return 0;
}
答案 8 :(得分:2)
已经提到使用函数来创建和处理结构。 但是在评论中你提到你想要一个“默认构造函数” - 我认为你的意思是你想要将一些(所有?)结构字段初始化为默认值。
这是在C中使用一些编码约定完成的 - 函数,宏或混合。 我通常会做类似以下的事情 -
struct some_struct {
int a;
float b;
};
#define some_struct_DEFAULT { 0, 0.0f}
struct some_struct *some_struct_create(void) {
struct some_struct *ptr = malloc(sizeof some_struct);
if(!ptr)
return ptr;
*ptr = some_struct_DEFAULT;
return ptr;
}
// (...)
struct some_struct on_stack = some_struct_DEFAULT;
struct some_struct *on_heap = some_struct_create();
答案 9 :(得分:2)
Fallowing是一个使用构造函数的简单程序。函数“default_constructor”在main中调用,没有任何显式函数调用。
#include <stdio.h>
void __attribute__ ((constructor)) default_constructor()
{
printf("%s\n", __FUNCTION__);
}
int main()
{
printf("%s\n",__FUNCTION__);
return 0;
}
输出: default_constructor 主
在构造函数中,你可以在构造函数中包含一些初始化语句,最后可以在析构函数中释放为fallow,
#include <stdio.h>
#include <stdlib.h>
struct somestruct
{
int empid;
char * name;
};
struct somestruct * str1;
void __attribute__ ((constructor)) a_constructor()
{
str1 = (struct somestruct *) malloc (sizeof(struct somestruct));
str1 -> empid = 30228;
str1 -> name = "Nandan";
}
void __attribute__ ((destructor)) a_destructor()
{
free(str1);
}
int main()
{
printf("ID = %d\nName = %s\n", str1 -> empid, str1 -> name);
return 0;
}
希望这会对你有所帮助。
答案 10 :(得分:1)
不是真的。如果我没记错的话,最接近课程的是结构。您分配内存并填充字段。我不知道你将如何为此创建一个通用的构造函数类型的东西。从理论上讲,你可以编写一个可以做到这一点的宏。不确定这是否真的值得。
答案 11 :(得分:1)
您可能想看一下C ++编译器。它为您提供了更多面向对象的功能,而不是您能够以更优雅和标准的方式使用具有类C语法的语言,而不是尝试使用库和宏将构造函数等用于C语言。
如果这对您不起作用,请查看GObject。 http://en.wikipedia.org/wiki/GObject。它是GTK和Gnome中使用的C的对象系统,它几乎将“C中的对象”曲解为11。
来自维基百科:
GLIB对象系统或GObject是一个免费软件库(由LGPL提供),它提供了一个便携式对象系统和透明的跨语言互操作性。
系统支持构造函数,析构函数,单继承,接口,虚拟公共和私有方法等。与在C ++中执行相同的操作相比,它也更加繁琐和困难。玩得开心!
答案 12 :(得分:0)
不,结构只不过是一堆数据。你不能在结构中声明函数,因此无法为它创建构造函数。