почему дескриптор объекта часто отображается как указатель на указатель - PullRequest
0 голосов
/ 28 августа 2018

Каково намерение установить дескриптор объекта как указатель на указатель, но не указатель? Как следующий код:

FT_Library library;
FT_Error error = FT_Init_FreeType( &library );

, где

typedef struct FT_LibraryRec_  *FT_Library

так &library - это FT_LIBraryRec_ ручка типа FT_LIBraryRec_**

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Функция библиотеки 'C' FT_Init_FreeType имеет два выхода: код ошибки и / или дескриптор библиотеки (который является указателем).

В C ++ мы бы более естественно:

  • возвращает объект, который инкапсулировал успех или неудачу вызова и дескриптор библиотеки, или

  • вернуть один вывод - дескриптор библиотеки и выдать исключение при ошибке.

C API, как правило, не реализованы таким образом.

Для функции библиотеки C нередко возвращать код успеха и передавать адреса переменных входа / выхода для условного изменения, как в случае выше.

0 голосов
/ 28 августа 2018

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

Вот как может быть реализована версия с одним указателем:

struct FT_Struct
{
    // Some fields/properties go here, e.g.
    int field1;
    char* field2;
}
FT_Error Init( FT_Struct* p )
{
    p->field1 = 11;
    p->field2 = malloc( 100 );
    if( nullptr == p->field2 )
        return E_OUTOFMEMORY;
    return S_OK;
}

или эквивалент C ++ без указателей:

class FT_Struct
{
    int field1;
    std::vector<char> field2;
public:
    FT_Struct() :
        field1( 11 )
    {
        field2.resize( 100 );
    }
};
  1. Как пользователь библиотеки, вы должны включить определение struct / class FT_Struct. Библиотеки могут быть очень сложными, поэтому это замедлит компиляцию вашего кода.
  2. Если библиотека динамическая, то есть * .dll в Windows, * .so в Linux или * .dylib в OSX, вы обновите библиотеку, и если новая версия изменит структуру памяти структуры / класса, старые приложения будут аварийно завершать работу.
  3. Из-за того, как работает C ++, объекты передаются по значению, т. Е. Обычно вы ожидаете, что они будут подвижными и копируемыми, что не обязательно то, что автор библиотеки хочет поддерживать.

Теперь рассмотрим следующую функцию:

FT_Error Init( FT_Struct** pp )
{
    try
    {
        *pp = new FT_Struct();
        return S_OK;
    }
    catch( std::exception& ex )
    {
        return E_FAIL;
    }
}

Как пользователь библиотеки, вам больше не нужно знать, что находится внутри FT_Struct или даже какого она размера. Вам не нужно #include детали реализации, то есть компиляция будет быстрее. Это прекрасно работает с динамическими библиотеками, автор библиотеки может изменять расположение памяти, как им угодно, если API C стабилен, старые приложения будут продолжать работать. API гарантирует, что вы не будете копировать или перемещать значения, вы не можете копировать структуры неизвестной длины.

0 голосов
/ 28 августа 2018

Это способ эмулировать передачу по ссылке в C, которая в противном случае имеет только передачу по значению.

...