Почему дескриптор объекта часто отображается как указатель на указатель

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

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

где

typedef struct FT_LibraryRec_  *FT_Library

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

3 ответа

Решение

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

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

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

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

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

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

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

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

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

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 гарантирует, что вы не будете копировать или перемещать значения, вы не можете копировать структуры неизвестной длины.

Другие вопросы по тегам