Construtor predefinido em C

existe uma forma de ter algum tipo de construtor predefinido (como um C++) para os tipos de utilizador C definidos com uma estrutura?

eu já tiver uma macro que funciona como um inicializador rápido (como o de pthread_mutex), mas eu queria saber se você pode por acaso tem algum (ou todos) os campos de uma estrutura preenchido na declaração.

Por exemplo, com o exemplo de pthread_mutex, gostaria de

pthread_mutex_t my_mutex;

para ter o mesmo efeito que

pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
Author: Jonathan Leffler, 2009-02-11

13 answers

Você pode criar funções de inicialização que levam um ponteiro para uma estrutura. Era uma prática comum.

Também funções que criam uma estrutura e inicializam-na (como uma fábrica) - por isso, nunca há um momento em que a estrutura é "desinicializada" no código "cliente". Claro-que assume que as pessoas seguem a Convenção e usam o "construtor" / fábrica...

Um pseudo-código horrível sem verificação de erros no malloc ou 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);
}
Provavelmente alguém virá. e explicar como alguns pré-processadores/compiladores de c++ trabalharam para fazer tudo isso em C.
 34
Author: Tim, 2009-02-11 15:26:01

C++ é diferente de C neste caso no que diz respeito a não ter "classes". No entanto, C (como muitas outras linguagens) ainda pode ser usado para programação orientada a objetos. Neste caso, seu construtor pode ser uma função que inicializa uma estrutura. Isto é o mesmo que Construtores (apenas uma sintaxe diferente). Outra diferença é que você tem que alocar o objeto usando malloc() (ou alguma variante). Em C++, você simlpy usaria o operador 'novo'.

Por exemplo, C++ código:

class A {
  public:
    A() { a = 0; }
    int a;
};

int main() 
{
  A b;
  A *c = new A;
  return 0;
}

Código C equivalente:

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

A função 'init_A_types' como um construtor faria em C++.

 19
Author: Reed, 2009-02-11 15:36:18
Vamos falar sobre a solução de engenharia completa que foi considerada a melhor prática nos velhos tempos. O problema das estruturas é que tudo é público, por isso não há dados escondidos. Podemos resolver isso.

Você cria dois ficheiros header. Um é o arquivo de cabeçalho "público" usado pelos clientes do seu código. Contém definições como esta:

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);

Depois cria um ficheiro de cabeçalho privado que contém definições como esta:

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;

E depois em sua implementação você pode fazer algo assim:

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

O que acontece é que você faz declarações de avanço para as suas estruturas em seus arquivos de cabeçalho público. As tuas estruturas são opacas, o que significa que os clientes não se podem meter com elas. Então, na declaração completa, você inclui um destruidor no início de cada estrutura que você pode chamar genericamente. Você pode usar o mesmo alocador malloc para todos a mesma função de eliminação e assim. Você torna públicas as funções set/get para os elementos queres ser exposto.

De repente, o teu código é muito mais são. Você só pode obter estruturas de alocadores ou funções que chamam alocadores, o que significa que você pode estrangular a inicialização. Você constrói destruidores para que o objeto possa ser destruído. E vai - te embora. A propósito, um nome melhor do que uma estrutura deficiente pode ser uma estrutura deficiente, porque é isso que é. Você pode agora construir herança virtual por ter uma vTableStruct que é todos os ponteiros de função. Você também pode fazer coisas que você não pode fazer em uma linguagem pura oo (tipicamente), como mudar elementos selecionados do vtable na mosca.

O ponto importante é que existe um padrão de engenharia para tornar as estruturas seguras e inicializáveis.

 10
Author: plinth, 2009-02-11 15:45:32
Não, Não directamente. O mais próximo que você poderia fazer é escrever uma função que alocou uma instância e povoou alguns dos Campos.
 8
Author: ConcernedOfTunbridgeWells, 2009-02-11 15:14:37

Você pode escrever uma função que devolve a estrutura C:

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

Se você se pergunta se você pode ter funções de membro "normal" em C. Bem, você pode até certo ponto. Prefiro o estilo objeto-como-primeiro-argumento. Você passa um ponteiro para sua estrutura como o primeiro argumento. Desta forma, poderá ter várias funções, definindo a interface para os seus objectos:

int file_get_integer(struct file *self) { return self->i; }
float file_get_float(struct file *self) { return self->f; }

Se você escrever nesse estilo, o que você tem no final é um tipo de dados abstrato. Já vi tipos a imitar o membro. sintaxe de chamada de função usada em C++, tendo ponteiros de função em sua estrutura e, em seguida, fazendo:

obj.get_integer(&obj);

É usado pelo kernel linux para definir a interface para os controladores de sistemas de ficheiros. É um estilo de escrita que se pode gostar, ou não gostar. Eu não gosto muito disso, porque eu continuo usando membros de estruturas para dados e não para emular chamadas de funções de membros para parecer em linguagens orientadas a objetos populares.

 4
Author: Johannes Schaub - litb, 2009-02-11 16:10:49

Basicamente, C++ cria listas de ponteiros que contêm os endereços dos métodos. Esta lista é chamada de definição de classe (há mais alguns dados na def de classe, mas nós ignoramos isso por enquanto).

Um padrão comum para ter " classes "em puro C é definir uma"classe struct". Um dos campos da estrutura é uma função de fábrica que retorna "instâncias" da classe. Sugiro usar uma macro para esconder os moldes.

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)))
Agora, podes definir as aulas que tens por criar estruturas de tipo "classe struct" para cada classe que você tem e, em seguida, chamar o construtor armazenado neles. O mesmo vale para o destruidor, etc.

Se você quer métodos para suas instâncias, deve colocá-los nas estruturas de classe e manter um ponteiro para a classe na estrutura de instância:

#define NEW_SOMETHING() ((struct something *)NEW(&something_definition))
#define METHOD(inst, arg) ((struct something_class *)(((struct something *)inst)->clz)->method(inst, arg))

NEW_SOMETHING irá criar uma nova instância de "algo" usando a definição de classe armazenada na estrutura something_definition. O método irá invocar um "método" nesta instância. Note que para código real, você vai querer verificar se inst é realmente uma instância de something (compare os ponteiros de classe, algo assim).

Ter herança é um pouco complicado e deixado como exercício para o leitor.

Lembre-se que pode fazer tudo o que puder em C++ em puro C. um compilador C++ não substitui magicamente a sua CPU por outra coisa. É preciso muito mais código para o fazer.

Se quiser ver um exemplo, veja glib (parte do projecto Gtk+).

 3
Author: Aaron Digulla, 2009-02-11 15:50:18

Assumindo que queres fazer isto em C, por isso a tua pergunta não é sobre estruturas em C++:

O que eu normalmente faço é que eu apenas crio uma função, init_whatever, que leva um ponteiro para a estrutura (ou outra variável), e define-a para os valores que eu quero.

Se é uma variável global (que é inicializada para zero) você poderia simular um construtor sem argumentos, tendo uma bandeira "inicializada" nela, e então deixar as suas outras funções verificar essa bandeira, e inicializar a estrutura se a bandeira for zero. Não tenho a certeza se isto é uma boa ideia.

E, como alguém notou, também se pode fazer algo horrível com macros...
 2
Author: Thomas Padron-McCarthy, 2009-02-11 15:34:08

Já foi mencionado o uso de funções para criar e eliminar estruturas. Mas em um comentário você mencionou que queria um "construtor padrão" - eu acho que você quer dizer que você quer inicializar alguns (Todos?) dos campos de struct aos valores padrão.

Isto é feito em C usando alguma convenção de codificação -- tanto funções, macros, ou uma mistura. Normalmente faço algo como o seguinte ...

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();
 2
Author: gnud, 2009-02-11 15:58:30
Aqui está um pouco de magia macro.malloc(), memcpy() e C99 literais compostos:
#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;
}
 2
Author: Christoph, 2009-02-11 16:07:38
A queda é o programa simples que faz uso de um construtor. A função "default_construtor" é chamada dentro da main sem qualquer chamada de função explícita.
#include        <stdio.h> 

void __attribute__ ((constructor)) default_constructor() 
{ 
    printf("%s\n", __FUNCTION__); 
} 

int main() 
{ 
    printf("%s\n",__FUNCTION__);
    return 0; 
}

Resultado: default_constructor principal

Dentro da função do construtor você pode incluir algumas declarações de inicialização na função do construtor e finalmente pode fazer libertando no destruidor como 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;
}
Espero que isto te ajude.
 2
Author: Nandan Bharadwaj, 2014-02-17 07:31:45
Na verdade, não. Se bem me lembro, o mais perto que se chega das aulas são estruturas. Você aloca a memória e preenche os campos. Não vejo como faria uma coisa de construtor genérico para isto. Teoricamente, você poderia escrever um macro que faria um pouco disso. Mas não sei se vale a pena.
 1
Author: paul, 2009-02-11 15:18:25

É melhor dar uma vista de olhos num compilador C++. Ele lhe dá mais características orientadas a objetos do que você pode se lembrar em uma linguagem com uma sintaxe Tipo C de uma forma mais elegante e padrão do que tentar parafusar construtores e assim por diante Em C com bibliotecas e macros.

Se isso não resultar para ti, dá uma olhadela ao GObject. http://en.wikipedia.org/wiki/GObject . é um sistema de objectos para C usado em GTK e Gnome que praticamente bloqueia "objectos em C" para 11.

Da Wikipédia:

O sistema de objetos GLib, ou GObject, é uma biblioteca de software livre (coberta pela LGPL) que fornece um sistema de objetos Portáteis e uma interoperabilidade transversal transparente.
O sistema suporta Construtores, destruidores, herança única, interfaces, métodos virtuais públicos e privados, etc. Também é muito mais entediante e difícil do que fazer as mesmas coisas em C++. Diverte-te!
 1
Author: joeforker, 2009-02-11 15:50:44
Não, as estruturas não passam de um monte de dados. Você não pode declarar funções dentro das estruturas, então não há maneira de criar um construtor para ele.
 0
Author: soulmerge, 2009-02-11 15:17:09