Отказ от ответственности: Я полный новичок с C, но я играл с ним, пытаясь имитировать некоторые функции классов. Хорошо, Я знаю, что если я хочу пойти таким путем, я должен изучить C++, но рассмотрим следующий небольшой эксперимент.Специи C с классами
Шрайнер, в книге объектно-ориентированное программирование с ANSI-C предлагает способ использования указателей, чтобы получить возможности ориентации объекта в C. Я должен признать, я обезжиренное только через книгу, но мне не нравится его подход слишком много. В основном, он использует указатели на функции для того, чтобы добиться того, что
func(foo);
фактически приводит вызова
foo.methods->func();
где foo.methods
является структура, содержащая указатели на функции. То, что мне не нравится в этом подходе, заключается в том, что в любом случае необходимо иметь глобальную функцию foo
; то есть методы не заменяются классом, в котором они живут. Я чувствую, что это скоро приведет к беспорядку: подумайте о двух объектах foo
и bar
, оба имеют метод func
, но с другим количеством параметров.
Так что я попытался получить что-то более подходящее на мой вкус. Первая попытка состоит в следующей (я опускаю заявление для краткости)
#include <stdio.h>
//Instances of this struct will be my objects
struct foo {
//Properties
int bar;
//Methods
void (* print)(struct foo self);
void (* printSum)(struct foo self, int delta);
};
//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
printf("This is bar: %d\n", self.bar);
}
static void printSumFoo(struct foo self, int delta) {
printf("This is bar plus delta: %d\n", self.bar + delta);
}
//This is a sort of constructor
struct foo Foo(int bar) {
struct foo foo = {
.bar = bar,
.print = &printFoo,
.printSum = &printSumFoo
};
return foo;
}
//Finally, this is how one calls the methods
void
main(void) {
struct foo foo = Foo(14);
foo.print(foo); // This is bar: 14
foo.printSum(foo, 2); // This is bar plus delta: 16
}
Это неудобное, но вроде работает. Однако мне не нравится, что вы должны явно добавить сам объект в качестве первого аргумента. С некоторыми работами препроцессора я могу сделать немного лучше:
#include <stdio.h>
#define __(stuff) stuff.method(* stuff.object)
//Instances of this struct will be my objects
struct foo {
//Properties
int bar;
//Methods
//Note: these are now struct themselves
//and they contain a pointer the object...
struct {
void (* method)(struct foo self);
struct foo * object;
} print;
};
//Here is the actual implementation of the methods
static void printFoo(struct foo self) {
printf("This is bar: %d\n", self.bar);
}
//This is a sort of constructor
struct foo Foo(int bar) {
struct foo foo = {
.bar = bar,
//...hence initialization is a little bit different
.print = {
.method = &printFoo,
.object = &foo
}
};
return foo;
}
//Finally, this is how one calls the methods
void
main(void) {
struct foo foo = Foo(14);
//This is long and unconvenient...
foo.print.method(* foo.print.object); // This is bar: 14
//...but it can be shortened by the preprocessor
__(foo.print); // This is bar: 14
}
Это насколько я могу получить. Проблема здесь в том, что она не будет работать для методов с аргументами, поскольку макросы препроцессора не могут принимать переменное количество аргументов. Конечно, можно определить макросы _0
, _1
и т. Д. В соответствии с количеством аргументов (до усталости), но это вряд ли хороший подход.
Есть ли способ улучшить это и позволить C использовать более объектно-ориентированный синтаксис?
Я должен добавить, что на самом деле Шрайнер делает гораздо больше, чем то, что я сказал в его книге, но я думаю, что базовая конструкция не меняется.
Я хотел бы использовать виртуальные таблицы подход к функциям, который похож на ваш второй подход, за исключением 'print' будет указателем. – leppie
«оба имеют метод func» ... func - это имя поля указателя на функцию в структуре: нет никакой причины, по которой глобальная функция, на которую указывает, должна называться просто «func». Префикс/постфикс это что-то специфичное для класса, и вы решили эту проблему. –
Макросы препроцессора могут принимать переменное количество аргументов, начиная с 12 лет назад. –