Сокрытие участников в структуре C

Я читал об ООП в C, но мне никогда не нравилось, как у Вас не может быть частных элементов данных как Вы, может в C++. Но затем это прибыло по моему мнению, что Вы могли создать 2 структуры. Каждый определяется в заголовочном файле, и другой определяется в исходном файле.

// =========================================
// in somestruct.h
typedef struct {
  int _public_member;
} SomeStruct;

// =========================================
// in somestruct.c

#include "somestruct.h"

typedef struct {
  int _public_member;
  int _private_member;
} SomeStructSource;

SomeStruct *SomeStruct_Create()
{
  SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
  p->_private_member = 42;
  return (SomeStruct *)p;
}

Отсюда можно просто бросить одну структуру к другому. Это считают плохой практикой? Или это часто делается?

61
задан Marlon 4 May 2013 в 16:07
поделиться

9 ответов

Лично мне больше нравится:

typedef struct {
  int _public_member;
  /*I know you wont listen, but don't ever touch this member.*/
  int _private_member;
} SomeStructSource;

В конце концов, это C, если люди хотят облажаться, им следует разрешить - не нужно чтобы скрыть вещи, кроме:

Если вам нужно сохранить совместимость с ABI / API, есть 2 подхода, которые более распространены из того, что я видел.

  • Не давайте своим клиентам доступ к структуре, дайте им непрозрачный дескриптор (void * с красивым именем), предоставьте функции инициализации / уничтожения и доступа для всего. Это гарантирует, что вы можете изменить структуру, даже не перекомпилируя клиентов, если вы пишете библиотеку.

  • предоставляют непрозрачный дескриптор как часть вашей структуры, которую вы можете размещать как угодно. Этот подход используется даже в C ++ для обеспечения совместимости с ABI.

например

 struct SomeStruct {
  int member;
  void* internals; //allocate this to your private struct
 };
36
ответ дан 24 November 2019 в 17:07
поделиться

Я бы написал скрытую структуру и ссылался на нее, используя указатель в открытой структуре. Например, ваш .h мог бы иметь:

typedef struct {
    int a, b;
    void *private;
} public_t;

А ваш .c:

typedef struct {
    int c, d;
} private_t;

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

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

Что-то похожее на предложенный вами метод действительно иногда используется (например, см. Различные варианты struct sockaddr * в API сокетов BSD), но его практически невозможно использовать, не нарушая строгих правил C99. правила наложения имен.

Однако вы можете сделать это безопасно :

somestruct.h :

struct SomeStructPrivate; /* Opaque type */

typedef struct {
  int _public_member;
  struct SomeStructPrivate *private;
} SomeStruct;

somestruct.c :

#include "somestruct.h"

struct SomeStructPrivate {
    int _member;
};

SomeStruct *SomeStruct_Create()
{
    SomeStruct *p = malloc(sizeof *p);
    p->private = malloc(sizeof *p->private);
    p->private->_member = 0xWHATEVER;
    return p;
}
7
ответ дан 24 November 2019 в 17:07
поделиться

У вас почти есть это, но вы еще не зашли достаточно далеко.

В заголовке:

struct SomeStruct;
typedef struct SomeStruct *SomeThing;


SomeThing create_some_thing();
destroy_some_thing(SomeThing thing);
int get_public_member_some_thing(SomeThing thing);
void set_public_member_some_thing(SomeThing thing, int value);

В .c:

struct SomeStruct {
  int public_member;
  int private_member;
};

SomeThing create_some_thing()
{
    SomeThing thing = malloc(sizeof(*thing));
    thing->public_member = 0;
    thing->private_member = 0;
    return thing;
}

... etc ...

Дело в том, что теперь потребители не имеют нет сведений о внутреннем устройстве SomeStruct, и вы можете безнаказанно изменять его, добавляя и удаление участников по желанию, даже без необходимости перекомпиляции потребителям. Они также не могут «случайно» изменить участников напрямую или разместить SomeStruct в стеке. Это, конечно, тоже можно рассматривать как недостаток.

25
ответ дан 24 November 2019 в 17:07
поделиться

Никогда этого не делайте.Если ваш API поддерживает все, что принимает SomeStruct в качестве параметра (чего я ожидаю), они могут выделить один в стеке и передать его. Вы получите серьезные ошибки, пытаясь получить доступ к закрытому члену, поскольку тот, который является компилятором выделяет для клиентского класса, не содержит для него места.

Классический способ скрыть члены в структуре - сделать ее пустой *. По сути, это дескриптор / файл cookie, о котором знают только ваши файлы реализации. Практически каждая библиотека C делает это для частных данных.

9
ответ дан 24 November 2019 в 17:07
поделиться

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

РЕДАКТИРОВАТЬ: Я пропустил, что это было в файле .c, но на самом деле ничто не мешает клиенту скопировать его или, возможно, даже #include напрямую вставить файл .c.

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

Есть более эффективные способы сделать это, например, использовать указатель void * на частную структуру в публичной структуре. То, как вы это делаете, вы обманываете компилятор.

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

Этот подход является допустимым, полезным, стандартным C.

Несколько иной подход, используемый API сокетов, который был определен в BSD Unix, - это стиль, используемый для структуры struct sockaddr .

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

sizeof (SomeStruct)! = sizeof (SomeStructSource) . Это заставит кого-нибудь найти вас и когда-нибудь убить.

47
ответ дан 24 November 2019 в 17:07
поделиться
Другие вопросы по тегам:

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