Как Вы реализуете класс в C? [закрытый]

Интерфейсные иерархии являются болью - они действительно "не наследовались" как таковой, так как у Вас может быть несколько "родителей" (из-за отсутствия лучшего термина).

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

interface ILow { void Low();}
interface IFoo : ILow { void Foo();}
interface IBar { void Bar();}
interface ITest : IFoo, IBar { void Test();}

static class Program
{
    static void Main()
    {
        List<Type> considered = new List<Type>();
        Queue<Type> queue = new Queue<Type>();
        considered.Add(typeof(ITest));
        queue.Enqueue(typeof(ITest));
        while (queue.Count > 0)
        {
            Type type = queue.Dequeue();
            Console.WriteLine("Considering " + type.Name);
            foreach (Type tmp in type.GetInterfaces())
            {
                if (!considered.Contains(tmp))
                {
                    considered.Add(tmp);
                    queue.Enqueue(tmp);
                }
            }
            foreach (var member in type.GetMembers())
            {
                Console.WriteLine(member.Name);
            }
        }
    }
}
131
задан Machavity 5 November 2018 в 20:06
поделиться

13 ответов

Это зависит от того, какой именно «объектно-ориентированный» набор функций вы хотите иметь. Если вам нужны такие вещи, как перегрузка и / или виртуальные методы, вам, вероятно, потребуется включить указатели функций в структуры:

typedef struct {
  float (*computeArea)(const ShapeClass *shape);
} ShapeClass;

float shape_computeArea(const ShapeClass *shape)
{
  return shape->computeArea(shape);
}

Это позволит вам реализовать класс, «наследуя» базовый класс и реализуя подходящую функцию:

typedef struct {
  ShapeClass shape;
  float width, height;
} RectangleClass;

static float rectangle_computeArea(const ShapeClass *shape)
{
  const RectangleClass *rect = (const RectangleClass *) shape;
  return rect->width * rect->height;
}

Это, конечно, требует, чтобы вы также реализовали конструктор, который обеспечивает правильную настройку указателя функции. Обычно вы должны динамически выделять память для экземпляра, но вы также можете позволить вызывающей стороне сделать это:

void rectangle_new(RectangleClass *rect)
{
  rect->width = rect->height = 0.f;
  rect->shape.computeArea = rectangle_computeArea;
}

Если вам нужно несколько разных конструкторов, вам придется «украсить» имена функций, у вас не может быть больше, чем одна функция rectangle_new () :

void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
  rectangle_new(rect);
  rect->width = width;
  rect->height = height;
}

Вот базовый пример, показывающий использование:

int main(void)
{
  RectangleClass r1;

  rectangle_new_with_lengths(&r1, 4.f, 5.f);
  printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
  return 0;
}

Я надеюсь, что это даст вам некоторые идеи, как минимум. Для успешной и богатой объектно-ориентированной структуры на C, загляните в библиотеку glib GObject .

Также обратите внимание, что здесь не моделируется явный «класс», каждый объект имеет свои собственные указатели на методы, которые являются немного более гибким, чем вы обычно найдете в C ++. Кроме того, это стоит памяти. Вы могли бы уйти от этого, вставив указатели методов в структуру class и изобрести способ для каждого экземпляра объекта ссылаться на класс.

83
ответ дан 24 November 2019 в 00:19
поделиться

Вам нужны виртуальные методы?

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

Если вы хотите иметь виртуальные методы, это становится более сложным. В основном вам нужно будет реализовать свой собственный VTable для каждой структуры и назначить указатели функций для VTable в зависимости от того, какая функция вызывается. Затем вам понадобится набор указателей на функции в самой структуре, которые, в свою очередь, вызывают указатель на функцию в VTable. По сути, это то, что делает C ++.

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

1
ответ дан 24 November 2019 в 00:19
поделиться

C не является языком ООП, как вы правильно заметили, поэтому нет встроенного способа написать настоящий класс. Лучше всего взглянуть на структуры и указатели на функции , они позволят вам построить приближение класса. Однако, поскольку C является процедурным, вы можете подумать о написании большего количества C-подобного кода (т.е. без попытки использовать классы).

Кроме того, если вы можете использовать C, вы, вероятно, можете использовать C ++ и получить классы.

0
ответ дан 24 November 2019 в 00:19
поделиться

Первый компилятор C ++ на самом деле был препроцессором, который транслировал код C ++ в C.

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

2
ответ дан 24 November 2019 в 00:19
поделиться

Мой подход заключался бы в перемещении struct и всех связанных в первую очередь функций в отдельный исходный файл (ы), чтобы что его можно использовать «переносимо».

В зависимости от вашего компилятора, вы могли иметь возможность включать функции в структуру , но это очень специфичное для компилятора расширение и не имеет ничего общего с последней версией стандарта, который я обычно использовал :)

2
ответ дан 24 November 2019 в 00:19
поделиться

См. Также этот ответ и этот

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

Скрытие данных с помощью функций get / set легко реализовать в C, но остановимся на этом. Я видел несколько попыток сделать это во встроенной среде, и в конце концов это всегда проблема обслуживания.

Поскольку у всех вас есть проблемы с обслуживанием, я бы держался подальше.

3
ответ дан 24 November 2019 в 00:19
поделиться

В вашем случае хорошим приближением класса может быть ADT . Но все равно не будет.

3
ответ дан 24 November 2019 в 00:19
поделиться

Невозможно, чтобы слияние (или перебазирование) могло работать без касания рабочего каталога (и индекса), поскольку могут возникнуть конфликты слияния, которые необходимо разрешить с помощью рабочего каталога (и / или index).

У вас всегда может быть другой клон (возможно, используя альтернативы или каталог объектов с символическими ссылками для экономии места на диске) или другой рабочий каталог с contrib / workdir / git-new-workdir . Или используйте такой инструмент, как ccache .

Изменить: в настоящее время git worktree является частью ядра Git, нет необходимости во внешних инструментах (по крайней мере, с git версии 2.6 .0).

потенциальные ловушки или скрытые преимущества / недостатки любого варианта этого подхода? Если я заново изобретаю метод дизайна (а я полагаю, что должен), можете ли вы указать мне его название?

потенциальные ловушки или скрытые преимущества / недостатки любого варианта этого подхода? Если я заново изобретаю метод дизайна (а я полагаю, что должен), можете ли вы указать мне его название?

3
ответ дан 24 November 2019 в 00:19
поделиться

Интерфейсы и реализации C: методы создания программного обеспечения многократного использования , Дэвид Р. Хэнсон

http://www.informit.com/store/product.aspx? isbn = 0201498413

Эта книга отлично справляется с ответом на ваш вопрос. Это из серии профессиональных вычислений Аддисона Уэсли.

Основная парадигма выглядит примерно так:

/* for data structure foo */

FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
7
ответ дан 24 November 2019 в 00:19
поделиться

Используйте структуру для имитации элементов данных класса. Что касается области действия метода, вы можете имитировать частные методы, поместив прототипы частных функций в файл .c и общедоступных функций в файл .h.

4
ответ дан 24 November 2019 в 00:19
поделиться

вы можете взглянуть на GOBject. это библиотека ОС, которая дает вам подробный способ создания объекта.

http://library.gnome.org/devel/gobject/stable/

7
ответ дан 24 November 2019 в 00:19
поделиться

Если вам нужен только один класс, используйте массив struct s в качестве данных "объектов" и передать указатели на них функциям-членам. Вы можете использовать typedef struct _whatever Whatever перед объявлением struct _whatever , чтобы скрыть реализацию от клиентского кода. Нет никакой разницы между таким «объектом» и объектом стандартной библиотеки C. FILE .

Если вам нужно несколько классов с наследованием и виртуальными функциями, то обычно указатели на функции являются членами структуры или общий указатель на таблицу виртуальных функций. Библиотека GObject использует как этот прием, так и трюк с определением типов, и широко используется.

Вот ' Кроме того, в Интернете доступна книга по методам этого решения - Объектно-ориентированное программирование с использованием ANSI C .

12
ответ дан 24 November 2019 в 00:19
поделиться

Мне тоже однажды пришлось сделать это в качестве домашней работы. Я использовал следующий подход:

  1. Определите элементы данных в struct.
  2. Определите ваши функциональные члены, которые возьмите указатель на вашу структуру как первый аргумент.
  3. Сделайте это в одном заголовке и одном c. Заголовок для определения структуры & объявления функций, c для реализации.

Простым примером может быть следующее:

/// Queue.h
struct Queue
{
    /// members
}
typedef struct Queue Queue;

void push(Queue* q, int element);
void pop(Queue* q);
// etc.
/// 
23
ответ дан 24 November 2019 в 00:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: