Подтвердить что ты не робот

Конструктор по умолчанию в C

Есть ли способ создать какой-либо конструктор по умолчанию (например, С++) для типов пользователей C, определенных со структурой?

У меня уже есть макрос, который работает как быстрый инициализатор (например, для pthread_mutex), но я хотел знать, можете ли вы каким-либо образом иметь (или все) поля структуры, заполненные в объявлении.

Например, с примером pthread_mutex мне бы хотелось

pthread_mutex_t my_mutex;

имеет тот же эффект, что и

pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
4b9b3361

Ответ 1

Вы можете создавать функции инициализатора, которые принимают указатель на структуру. Это была обычная практика.

Также функции, которые создают структуру и инициализируют ее (например, factory), так что никогда не бывает, когда структура "неинициализирована" в "клиентском" коде. Конечно, это предполагает, что люди следуют этому соглашению и используют "конструктор" /factory...

ужасный псевдокод с ошибкой проверки ошибок на 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.

Ответ 2

С++ отличается от C в этом случае тем, что у него нет "классов". Однако C (как и многие другие языки) все еще можно использовать для объектно-ориентированного программирования. В этом случае ваш конструктор может быть функцией, которая инициализирует структуру. Это то же самое, что и конструкторы (только другой синтаксис). Другое отличие состоит в том, что вам нужно выделить объект, используя malloc() (или какой-то вариант). В С++ вы могли бы использовать "новый" оператор.

например. Код С++:

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' как конструктор будут в С++.

Ответ 3

Расскажите о полном инженерном решении, которое считалось лучшей практикой в ​​старину.

Проблема с structs заключается в том, что все является общедоступным, поэтому нет скрытия данных.

Мы можем исправить это.

Вы создаете два файла заголовка. Один из них - это "общедоступный" заголовочный файл, используемый клиентами вашего кода. Он содержит такие определения:

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 для всех одинаковой функции размещения и так далее. Вы делаете общедоступные функции set/get для элементов, которые вы хотите открыть.

Внезапно ваш код станет более разумным. Вы можете получить только структуры от распределителей или функции, которые назначают распределители, что означает, что вы можете инициализировать узкие места. Вы создаете деструкторы, чтобы объект мог быть уничтожен. И по тебе. Кстати, лучшим именем t_DisposableStruct может быть t_vTableStruct, потому что это то, чем оно является. Теперь вы можете создавать виртуальное наследование, имея vTableStruct, который является всеми указателями на функции. Вы также можете делать то, что вы не можете сделать на чистом языке (обычно), например, при изменении элементов выбора vtable на лету.

Важным моментом является то, что существует инженерный шаблон для обеспечения безопасности и инициализации структур.

Ответ 4

Нет, не напрямую. Самое близкое, что вы можете сделать, это написать функцию, которая выделила экземпляр и заполнила некоторые поля.

Ответ 5

Вы можете написать функцию, которая возвращает C struct:

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

Если вы пишете в этом стиле, то, что у вас есть в конце, является абстрактным типом данных. Я видел, как ребята эмулируют синтаксис вызова функции-члена, используемый в С++, указав указатели на объекты в свою структуру, а затем:

obj.get_integer(&obj);

Он используется ядром linux для определения интерфейса для драйверов файловой системы. Это стиль письма, который может понравиться или может не понравиться. Мне это не очень нравится, потому что я продолжаю использовать элементы структур для данных, а не для эмуляции вызовов функций-членов, которые выглядят на популярных объектно-ориентированных языках.

Ответ 6

В принципе, С++ создает списки указателей, которые содержат адреса методов. Этот список называется определением класса (в классе def есть еще несколько данных, но на данный момент мы игнорируем это).

Общим шаблоном для "классов" в чистом C является определение "класса структуры". Одним из полей структуры является функция factory, которая возвращает "экземпляры" класса. Я предлагаю использовать макрос, чтобы скрыть приведения:

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" для каждого класса, который у вас есть, а затем вызовите хранимый в них конструктор. То же самое касается деструктора и т.д.

Если вам нужны методы для ваших экземпляров, необходимо поместить их в структуры классов и сохранить указатель на класс в экземпляре struct:

#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. Компилятор С++ не волшебным образом заменяет ваш процессор чем-то другим. Для этого требуется гораздо больше кода.

Если вы хотите посмотреть пример, посмотрите glib (часть проекта Gtk +).

Ответ 7

Предполагая, что вы хотите сделать это на C, ваш вопрос не о структурах на С++:

Что я обычно делаю, так это то, что я просто создаю функцию init_whatever, которая берет указатель на структуру (или другую переменную) и устанавливает ее в значения, которые я хочу.

Если это глобальная переменная (которая инициализируется нулем), вы можете сортировать синтаксис конструктора без аргументов, имея в нем "инициализированный" флаг, а затем пусть ваши другие функции проверяют этот флаг и инициализируют структуру если флаг равен нулю. Я даже не уверен, что это хорошая идея.

И, как заметил кто-то другой, вы также можете сделать что-то ужасное с помощью макросов...

Ответ 8

Здесь небольшая макромагия вокруг 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;
}

Ответ 9

Использование функций для создания и удаления структур уже упоминалось. Но в комментарии вы упомянули, что вам нужен "конструктор по умолчанию" - я думаю, вы хотите, чтобы вы инициализировали некоторые (все?) Поля структуры значениями по умолчанию.

Это делается на 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();

Ответ 10

Не совсем. Если я правильно помню, ближе к классам относятся структуры. Вы выделяете память и заполняете поля. Я не вижу, как вы делаете для этого тип типа конструктора. Теоретически, вы могли бы написать макрос, который бы сделал некоторые из этого. Не уверен, что это действительно стоит.

Ответ 11

Возможно, вы захотите взглянуть на компилятор С++. Это дает вам больше объектно-ориентированных функций, чем вы можете вспомнить на языке с синтаксисом типа C более элегантным и стандартным способом, чем пытаться закрепить конструкторы и т.д. На C с библиотеками и макросами.

Если это не сработает для вас, посмотрите на GObject. http://en.wikipedia.org/wiki/GObject. Это объектная система для C, используемая в GTK и Gnome, что довольно много кривых "объекты в C" до 11.

Из Википедии:

Система GLib Object или GObject - это библиотека бесплатного программного обеспечения (охватываемая LGPL), которая обеспечивает переносимую объектную систему и прозрачную межъязыковую совместимость.

Система поддерживает конструкторы, деструкторы, единое наследование, интерфейсы, виртуальные публичные и частные методы и т.д. Это также намного более утомительно и сложно, чем делать то же самое на С++. Получайте удовольствие!

Ответ 12

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 Основной

Внутри функции конструктора вы можете включить некоторые инструкции инициализации в конструкторскую функцию и, наконец, сделать освобождение в деструкторе парой,

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

Надеюсь, это поможет вам.

Ответ 13

Нет, структуры - это всего лишь куча данных. Вы не можете объявлять функции внутри структур, поэтому нет возможности создать для него конструктор.