Отказ от ответственности: я полный новичок с C, но я играл с ним, пытаясь имитировать некоторые функции classов. Хорошо, я знаю, что если я хочу пойти таким путем, я должен изучить C ++, но рассмотрим следующий небольшой эксперимент .
Шрейнер, в книге Объектно-ориентированное программирование с ANSI-C предлагает способ использования указателей для получения объектов ориентации объектов в C. Я должен признать, что я только просматривал книгу, но мне не нравится его подход слишком много. В принципе, он использует указатели на функции, чтобы
func(foo);
фактически приводит к вызову
foo.methods->func();
где foo.methods
– это структура, содержащая указатели на функции. То, что мне не нравится в этом подходе, состоит в том, что в любом случае нужно иметь глобальную функцию foo
; то есть методы не заменяются именами classа, в котором они живут. Я чувствую, что это скоро приведет к беспорядку: подумайте о двух объектах foo
и bar
, оба имеют метод func
но с другим количеством параметров.
Поэтому я попытался получить что-то более подходящее на мой вкус. Первая попытка заключается в следующем (я пропущу декларации ради краткости)
#include //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 #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 }
Это насколько я могу получить. Проблема здесь в том, что она не будет работать для методов с аргументами, поскольку macros препроцессора не могут принимать переменное количество аргументов. Конечно, можно определить macros _0
, _1
и т. Д. В соответствии с количеством аргументов (до усталости), но это вряд ли хороший подход.
Есть ли способ улучшить это и позволить C использовать более объектно-ориентированный синтаксис?
Я должен добавить, что на самом деле Шрайнер делает гораздо больше, чем то, что я сказал в своей книге, но я думаю, что базовая конструкция не меняется.
Различные frameworks уже существуют. См. Например, http://ldeniau.web.cern.ch/ldeniau/html/oopc.html
Книга (в формате PDF), которая объясняет, как это сделать, является объектно-ориентированным программированием в ANSI C. Она старая (1993), но содержит некоторые действительные идеи и советы, IMHO.
Вы взглянули на Google Go? Это, в основном, модернизированный C, где делаются несколько, как вы предлагали. Он имеет параметрические polymorphismы. Поэтому вам не нужно это делать:
static void printFoo(struct foo self) { printf("This is bar: %d\n", self.bar); }
В Go это можно сделать следующим образом:
static void print(Foo self) { printf("This is foo: %d\n", self.foo); } static void print(Bar self) { printf("This is bar: %d\n", self.bar); }
В Go Foo и Bar также будут структуры. Таким образом, вы, в основном, являетесь разработчиком языка Go.
Обзор Go см. http://golang.org/doc/effective_go.html Это описание основного языка Go: http://golang.org/doc/effective_go.html