C中的默认构造函数

时间:2009-02-11 15:11:21

标签: c constructor

对于使用结构定义的C用户类型,是否有某种方法可以使用某种默认构造函数(如C ++版本)?

我已经有一个像快速初始化程序一样工作的宏(就像pthread_mutex那样),但我想知道你是否可以在声明中填充结构的某些(或所有)字段。

例如,使用pthread_mutex示例,我想

pthread_mutex_t my_mutex;

具有相同的效果
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;

13 个答案:

答案 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)

在这种情况下,C ++与C不同,它没有“类”。但是,C(与许多其他语言一样)仍然可以用于面向对象的编程。在这种情况下,构造函数可以是初始化结构的函数。这与构造函数相同(只有不同​​的语法)。另一个区别是你必须使用malloc()(或某些变体)分配对象。在C ++中,你可以使用'new'运算符。

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)

不,结构只不过是一堆数据。你不能在结构中声明函数,因此无法为它创建构造函数。