Полиморфизм в С - PullRequest
       4

Полиморфизм в С

5 голосов
/ 16 декабря 2010

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

Ответы [ 3 ]

13 голосов
/ 16 декабря 2010

Вы обычно делаете это с помощью указателей на функции.Другими словами, простые структуры, которые содержат как данные , так и , указывают на функции, которые манипулируют этими данными.Мы занимались такими вещами за годы до того, как на сцену вышел Bjarne S.

Так, например, в классе связи у вас будет вызов open, read, write и close, который будет поддерживаться равным четыремуказатели на функции в структуре, наряду с данными для объекта, что-то вроде:

typedef struct {
    int (*open)(void *self, char *fspec);
    int (*close)(void *self);
    int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    // And the data for the object goes here.
} tCommsClass;

tCommsClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommsClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

Инициализация этих указателей на функции фактически будет в "конструкторе", таком как rs232Init(tCommClass*), который будет отвечать заустановка состояния по умолчанию этого конкретного объекта для соответствия определенному классу.

Когда вы «наследуете» от этого класса, вы просто меняете указатели, чтобы они указывали на ваши собственные функции.Каждый, кто вызывал эти функции, делал бы это через указатели на функции, давая вам ваш полиморфизм:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

Вроде как настроенный вручную vtable , на языке C ++.

Вы могли бы даже иметь виртуальные классы, установив указатели на NULL - поведение будет немного отличаться от C ++, поскольку вы, вероятно, получите дамп ядра во время выполнения, а не ошибку во время компиляции.

Вотфрагмент примера кода, который демонстрирует это:

#include <stdio.h>

// The top-level class.

typedef struct _tCommClass {
    int (*open)(struct _tCommClass *self, char *fspec);
} tCommClass;

// Function for the TCP class.

static int tcpOpen (tCommClass *tcp, char *fspec) {
    printf ("Opening TCP: %s\n", fspec);
    return 0;
}
static int tcpInit (tCommClass *tcp) {
    tcp->open = &tcpOpen;
    return 0;
}

// Function for the HTML class.

static int htmlOpen (tCommClass *html, char *fspec) {
    printf ("Opening HTML: %s\n", fspec);
    return 0;
}
static int htmlInit (tCommClass *html) {
    html->open = &htmlOpen;
    return 0;
}

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHtml;

    // Same base class but initialized to different sub-classes.
    tcpInit (&commTcp);
    htmlInit (&commHtml);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHtml.open)(&commHtml, "http://www.microsoft.com");

    return 0;
}

Это приводит к выводу:

Opening TCP: bigiron.box.com:5000
Opening HTML: http://www.microsoft.com

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

1 голос
/ 16 декабря 2010

Я удивлен, никто не упомянул glib, gtk и систему GObject.Так что вместо выпечки еще один-другой-слой-на-C.Почему бы не использовать то, что доказало свою эффективность?

С уважением, Фридрих

0 голосов
/ 16 декабря 2010

Люди совершали глупые поступки с различными типами struct s и полагались на предсказуемое заполнение - например, вы можете определить struct с определенным подмножеством другого struct, и это обычно будет работать. Смотрите ниже (код украден из Википедии):

struct ifoo_version_42 {
   long x, y, z;
   char *name;
   long a, b, c;
};
struct ifoo_old_stub {
   long x, y;
};
void operate_on_ifoo(struct ifoo_version_42 *);
struct ifoo_old_stub s;
...
operate_on_ifoo(&s);

В этом примере ifoo_old_stub можно считать суперклассом. Как вы, вероятно, можете понять, это зависит от того факта, что один и тот же компилятор будет дополнять две структуры одинаково, и попытка доступа к x и y в версии 42 будет работать, даже если вы передадите заглушку. Это должно работать и в обратном направлении. Но AFAIK это не обязательно работает через компиляторы, поэтому будьте осторожны, если вы хотите отправить структуру этого формата по сети, или сохранить ее в файле, или вызвать библиотечную функцию с одним из них.

Есть причина, по которой полиморфизм в C ++ довольно сложно реализовать ... (vtables и т. Д.)

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