2011-01-19 3 views
9

Отказ от ответственности: Я полный новичок с 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 использовать более объектно-ориентированный синтаксис?

Я должен добавить, что на самом деле Шрайнер делает гораздо больше, чем то, что я сказал в его книге, но я думаю, что базовая конструкция не меняется.

+0

Я хотел бы использовать виртуальные таблицы подход к функциям, который похож на ваш второй подход, за исключением 'print' будет указателем. – leppie

+0

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

+2

Макросы препроцессора могут принимать переменное количество аргументов, начиная с 12 лет назад. –

ответ

2

Различные рамки уже существуют. Смотри, например, http://ldeniau.web.cern.ch/ldeniau/html/oopc.html

+0

Спасибо, это выглядит очень интересно. Я посмотрю на источник, чтобы увидеть, как это работает. – Andrea

+0

500 - Внутренняя ошибка сервера При загрузке запрошенной страницы произошла ошибка: http: //ldeniau.web .cern.ch/ldeniau/html/oopc.html - Существует проблема с ресурсом, который вы ищете, и его невозможно отобразить. – lpapp

+0

@ LaszloPapp, у Лорана были бумаги (вы можете найти один или два, я) и несколько фреймворков, связанных с его домашней страницей. Похоже, что одна из его фреймворков доступна по адресу http://sourceforge.net/projects/cos/ (также есть проект githup, который кажется неактивным). – AProgrammer

1

Книга (в виде PDF), который объясняет, как это сделать, это object oriented programming in ANSI C Это старая (1993), но все еще содержит некоторые действительные идеи и советы, ИМХО.

+0

Действительно, это книга, на которую я ссылаюсь, но мне не нравится ее подход слишком много. См. Вопрос. – Andrea

+0

ОК, я не видел ссылку сначала! –

1

Был ли у вас взгляд на Google Go? Это, в основном, модернизированный C, где делаются несколько, как вы предлагали. Он имеет параметрические полиморфизмы. Таким образом, вы не должны делать это:

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

Смежные вопросы