Правильный способ реализации конструктора в C - PullRequest
2 голосов
/ 22 апреля 2020

Я хочу создать конструктор, используя C. Какой самый чистый способ добиться этого?

Моя попытка:

#include <stdio.h>

struct line {
    void (*add_line_ptr)(struct line*, int x, int y);
    int x;
    int y;
};

void add_line(struct line* lptr, int x, int y)
{
    lptr->x = x;
    lptr->y = y;
}


int main()
{
    struct line line1 = {add_line, 0, 0};
    line1.add_line_ptr(&line1, 10, 20);
    printf("%d %d\n", line1.x, line1.y);
}

Я думаю, что использование line1.add_line(&line1, немного избыточно - поскольку совершенно очевидно, что я хочу выполнить операцию на линии1. Есть ли способ реализовать это без передачи указателя на «объект» (структуру)? или каким-то другим способом, о котором я не думал?

Ответы [ 2 ]

5 голосов
/ 22 апреля 2020

Использование указателей на функции просто для эмуляции C ++ - подобный синтаксис просто беспорядочный, без очевидных преимуществ. У вас не будет RAII в C, независимо от того, что вы делаете, поэтому вам нужно явно вызывать конструкторы / деструкторы. Если вы делаете это, набирая obj.foo() или obj = foo(), то не имеете абсолютно ничего общего с ОО, это просто стиль кодирования.

Основная проблема здесь заключается в том, что ваш код не имеет надлежащего дизайна ОО, так как структура полностью открыта и не использует приватную инкапсуляцию. По той же причине, что class line { public: int x; int y; }; также не является правильным ОО - вы не получаете ОО только потому, что вы вводите sh некоторые связанные переменные в агрегатный тип независимо от языка.

"Cleanest" / " самый красивый "будет означать полную приватную инкапсуляцию. В C это может быть достигнуто с непрозрачными типами. Я предпочитаю реализовывать их, не пряча указатели за typedef, поэтому:

line.h

#include <stdio.h>
#include <stdlib.h>

typedef struct line line; // forward declaration of incomplete type

line* line_construct (int x, int y);

line. c

#include "line.h"

struct line {    // actual definition of the struct, local to line.c
    int x;       // private variable
    int y;       // private variable
};

line* line_construct (int x, int y)
{
  line* obj = malloc (sizeof *obj); 
  if(obj == NULL) { /* error handling here */ } 

  obj->x = x;
  obj->y = y;

  return obj;
}

вызывающий. c

#include "line.h"

int main(void)
{
    line* x = line_construct(10, 20);
}

Здесь line инкапсулирован на 100%, и вызывающий не может получить доступ к содержимому структуры. Поскольку я не скрываю указатели за typedef, вызывающая сторона всегда должна использовать указатели line* и никогда не может объявить экземпляр объекта напрямую.

Если конструктор предназначен только для обнуления членов структуры, тогда ему не нужно передавать какие-либо параметры, но он может делать это внутренне.

И, очевидно, вам также нужно реализовать соответствующий деструктор с free.
line* line_destruct(line* obj) { free(obj); return NULL; } или около того.

2 голосов
/ 22 апреля 2020

Я бы не стал вводить инициализатор в вашу структуру с самого начала. Другие функции, конечно, но не первая. Примерно так:

#include <stdio.h>

struct line {
    int x;
    int y;
};

void add_line(struct line *lptr, int x, int y)
{
    lptr->x = x;
    lptr->y = y;
}


int main()
{
    struct line line1 = {0, 0};
    add_line(&line1, 10, 20);
    printf("%d %d\n", line1.x, line1.y);
}

Причина в том, что вы должны назначить инициализатор в любом случае после того, как выделите свою структуру, неявно или явно. В OOP языках вы обычно называете нужный класс один раз, и запускаются как распределитель, так и инициализатор. В C вы должны запускать их отдельно. Независимо от того, размещаете ли вы объект в стеке или в куче, вам все равно придется явно назвать функцию, которую вы хотите вызвать хотя бы один раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...