Если тип никогда не станет более сложным, чем long double
, то, вероятно, не стоит иметь намерений скрывать его больше. Если это может потребоваться усложнить, тогда вы можете рассмотреть возможность использования непрозрачного типа. В вашем общедоступном заголовке big.h
вы используете:
#ifndef BIG_H_INCLUDED
#define BIG_H_INCLUDED
typedef struct big big_t;
extern big_t *foo(int);
...
#endif
Все функции будут принимать и возвращать указатели на тип big_t
. Это все, что вы можете сделать с такими неполными типами. Обратите внимание, что ваши клиенты не могут выделять для себя какие-либо значения big_t
; они не знают, какой размер у этого типа. Это означает, что вы, вероятно, в конечном итоге с такими функциями, как:
extern big_t *big_create(void);
extern void big_destroy(big_t *value);
для создания и уничтожения big_t
значений. Тогда они смогут выполнять арифметику с:
extern big_errno_t big_add(const big_t *lhs, const big_t *rhs, big_t *result);
Etc. Но поскольку они имеют только непрозрачный, неполный тип, они не могут надежно возиться внутри структуры big_t
. Но обратите внимание, что вы ограничены использованием указателей в интерфейсе. Для передачи или возврата значений требуются полные типы, и если тип полон, пользователи могут исследовать его внутреннюю работу.
В заголовке реализации bigimpl.h
вы получите:
#ifndef BIGIMPL_H_INCLUDED
#define BIGIMPL_H_INCLUDED
#include "big.h"
struct big
{
...whatever the details actually are...
};
#endif
И ваш код реализации будет включать только bigimpl.h
, но это включает big.h
. Основная проблема здесь - убедиться, что вы знаете, как распределяются выделения памяти.
Иногда эта техника стоит того. Часто это не совсем необходимо. Вам нужно будет сделать оценку для себя.