Отказ от ответственности: я новичок в 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 использовать более объектно-ориентированный синтаксис?
Я должен добавить, что на самом деле Шрайнер делает гораздо больше, чем я говорил в его книге, но я думаю, что базовая конструкция не меняется.