Подход скрывает реализацию. Это ускоряет компиляцию вашего кода. Это позволяет обновлять структуры данных, используемые библиотекой, не нарушая существующий код, который их использует. Наконец, он гарантирует, что адрес этого объекта никогда не изменится, и что вы не будете копировать эти объекты.
Вот как может быть реализована версия с одним указателем:
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 );
}
};
- Как пользователь библиотеки, вы должны включить определение struct / class FT_Struct. Библиотеки могут быть очень сложными, поэтому это замедлит компиляцию вашего кода.
- Если библиотека динамическая, то есть * .dll в Windows, * .so в Linux или * .dylib в OSX, вы обновите библиотеку, и если новая версия изменит структуру памяти структуры / класса, старые приложения будут аварийно завершать работу.
- Из-за того, как работает 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 гарантирует, что вы не будете копировать или перемещать значения, вы не можете копировать структуры неизвестной длины.