Давайте поговорим о полном инженерном решении, которое считалось лучшей практикой в старину.
Проблема с структурами заключается в том, что все является общедоступным, поэтому нет скрытия данных.
Мы можем исправить это.
Вы создаете два файла заголовка. Один из них - это «общедоступный» заголовочный файл, используемый клиентами вашего кода. Он содержит определение, как это:
typedef struct t_ProcessStruct *t_ProcessHandle;
extern t_ProcessHandle NewProcess();
extern void DisposeProcess(t_ProcessHandle handle);
typedef struct t_PermissionsStruct *t_PermissionsHandle;
extern t_PermissionsHandle NewPermissions();
extern void DisposePermissions(t_PermissionsHandle handle);
extern void SetProcessPermissions(t_ProcessHandle proc, t_PermissionsHandle perm);
затем создать частный заголовочный файл, который содержит определение, как это:
typedef void (*fDisposeFunction)(void *memoryBlock);
typedef struct {
fDisposeFunction _dispose;
} t_DisposableStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PID _pid;
/* etc */
} t_ProcessStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PERM_FLAGS _flags;
/* etc */
} t_PermissionsStruct;
, а затем в вашей реализации вы можете сделать что-то вроде этого:
static void DisposeMallocBlock(void *process) { if (process) free(process); }
static void *NewMallocedDisposer(size_t size)
{
assert(size > sizeof(t_DisposableStruct);
t_DisposableStruct *disp = (t_DisposableStruct *)malloc(size);
if (disp) {
disp->_dispose = DisposeMallocBlock;
}
return disp;
}
static void DisposeUsingDisposer(t_DisposableStruct *ds)
{
assert(ds);
ds->_dispose(ds);
}
t_ProcessHandle NewProcess()
{
t_ProcessHandle proc = (t_ProcessHandle)NewMallocedDisposer(sizeof(t_ProcessStruct));
if (proc) {
proc->PID = NextPID(); /* etc */
}
return proc;
}
void DisposeProcess(t_ProcessHandle proc)
{
DisposeUsingDisposer(&(proc->_disposer));
}
Что происходит, так это то, что вы отправляете декларации для своих структур в свои общедоступные файлы заголовков. Теперь ваши структуры непрозрачны, а это значит, что клиенты не могут с ними справиться. Затем в полном объявлении вы включаете деструктор в начале каждой структуры, которую вы можете назвать в общем. Вы можете использовать один и тот же распределитель malloc для всех одинаковой функции размещения и так далее. Вы делаете публичные функции set/get для элементов, которые вы хотите открыть.
Внезапно ваш код становится более разумным. Вы можете получить только структуры из распределителей или функции, которые назначают распределители, что означает, что вы можете инициализировать узкие места. Вы создаете деструкторы, чтобы объект мог быть уничтожен. И по тебе. Кстати, лучшим именем, чем t_DisposableStruct, может быть t_vTableStruct, потому что это то, что есть. Теперь вы можете создавать виртуальное наследование, имея vTableStruct, который является всеми указателями на функции. Вы также можете делать то, что вы не можете сделать на чистом языке (обычно), например, с изменением элементов выбора vtable на лету.
Важным моментом является то, что является инженерным шаблоном для создания структур безопасным и инициализируемым.
О, сэр, как мы скучаем по тебе. – joeforker
... как ужасная горящая сыпь. – Randolpho
И как cfront обрабатывал конструктор в начале? – claf