代码之家  ›  专栏  ›  技术社区  ›  claf

C中的默认构造函数

  •  30
  • claf  · 技术社区  · 17 年前

    pthread_mutex )但我想知道是否有可能在声明时填充结构体的某些(或所有)字段。

    例如,我想

    pthread_mutex_t my_mutex;
    

    具有与相同的效果

    pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
    
    13 回复  |  直到 11 年前
        1
  •  41
  •   Tim    17 年前

    您可以创建初始化器函数,该函数将指针指向一个结构。这是常见的做法。

    还有创建结构并初始化它的函数(就像工厂一样),因此在“客户端”代码中,结构永远不会“未初始化”。当然,这假设人们遵循惯例并使用“构造函数”/工厂。..

    在malloc或free上没有错误检查的可怕伪代码

    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中完成这一切的。

        2
  •  21
  •   Reed    17 年前

    在这种情况下,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;
    }
    

    等效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++中作为构造函数运行。

        3
  •  12
  •   plinth    17 年前

    让我们来谈谈在过去被认为是最佳实践的完整工程解决方案。

    您将创建两个头文件。一个是代码客户端使用的“公共”头文件。它包含这样的定义:

    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来构建虚拟继承,vTableStrutt是所有函数指针。你也可以做一些在纯面向对象语言中无法做到的事情(通常),比如动态更改vtable的选定元素。

    关键在于 一种使结构安全且可初始化的工程模式。

        4
  •  8
  •   ConcernedOfTunbridgeWells    17 年前

    不,不是直接的。你能做的最接近的事情是编写一个函数,分配一个实例并填充一些字段。

        5
  •  5
  •   Johannes Schaub - litb    17 年前

    您可以编写一个返回C结构体的函数:

    struct file create_file(int i, float f) {
        struct file obj = { i, f };
        // other code here...
        return obj;
    }
    

    如果你想知道你是否可以在C中拥有“正常”的成员函数,那么在某种程度上你可以。我更喜欢对象作为第一论证风格。将指向结构体的指针作为第一个参数传递。这样,您可以有几个函数,定义对象的接口:

    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内核使用它来定义文件系统驱动程序的接口。这是一种人们可能喜欢或不喜欢的写作风格。我不太喜欢它,因为我一直使用结构的成员来处理数据,而不是模拟成员函数调用,使其看起来像流行的面向对象语言。

        6
  •  3
  •   Aaron Digulla    17 年前

    基本上,C++创建了包含方法地址的指针列表。这个列表被称为类定义(类定义中还有一些更多的数据,但我们暂时忽略它)。

    在纯C中有“类”的一个常见模式是定义一个“结构类”。结构体的一个字段是一个工厂函数,它返回类的“实例”。我建议使用宏来隐藏演员阵容:

    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”的新实例 something_definition inst 实际上是 something (比较类指针,诸如此类)。

    继承有点棘手,留给读者练习。

    请注意,你可以在纯C中用C++做任何事情。C++编译器不会神奇地用其他东西替换你的CPU。这只需要更多的代码来完成。

    如果你想看一个例子,请查看 glib (Gtk+项目的一部分)。

        7
  •  2
  •   Thomas Padron-McCarthy    17 年前

    假设你想在C中这样做,那么你的问题不是关于C++中的结构:

    我通常会创建一个函数init_whatever,它接受一个指向结构(或其他变量)的指针,并将其设置为我想要的值。

    如果它是一个全局变量(初始化为零),则 可以 通过在无参数构造函数中添加“初始化”标志来模拟它,然后让其他函数检查该标志,如果该标志为零,则初始化该结构。不过,我一点也不确定这是个好主意。

    而且,正如其他人所指出的,你也可以用宏做一些可怕的事情。..

        8
  •  2
  •   gnud    17 年前

    但在一条评论中,你提到你想要一个“默认构造函数”——我想你的意思是你想将一些(所有?)结构字段初始化为默认值。

    这是在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
  •   Christoph    17 年前

    malloc() , memcpy()

    #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;
    }
    
        10
  •  2
  •   Community Mohan Dere    6 年前

    #include        <stdio.h> 
    
    void __attribute__ ((constructor)) default_constructor() 
    { 
        printf("%s\n", __FUNCTION__); 
    } 
    
    int main() 
    { 
        printf("%s\n",__FUNCTION__);
        return 0; 
    }
    

    输出: default_constructor 主要的

    在构造函数中,您可以包含一些初始化语句,并使用析构函数函数释放内存,如下所示:,

    #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;
    }
    

        11
  •  1
  •   paul    17 年前

    不是真的。如果我没记错的话,你最接近类的是结构。您可以分配内存并填充字段。我不明白你会如何为此制作一个泛型构造函数类型的东西。理论上,你可以编写一个宏来完成其中的一些任务。但不确定这是否真的值得。

        12
  •  1
  •   joeforker    17 年前

    如果这对你不起作用,那么看看GObject。 http://en.wikipedia.org/wiki/GObject 它是GTK和Gnome中使用的C对象系统,几乎可以将“C中的对象”转换为11。

    GLib对象系统或GObject是一个自由软件库(由LGPL涵盖),它提供了一个可移植的对象系统和透明的跨语言互操作性。

    该系统支持构造函数、析构函数、单继承、接口、虚拟公共和私有方法等。这也比用C++做同样的事情要乏味和困难得多。玩得开心

        13
  •  0
  •   soulmerge    17 年前

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