Существует очень реальная проблема с совместно используемыми библиотеками, которую идиома pimpl обходит аккуратно, чего не могут чистые виртуалы: вы не можете безопасно изменять / удалять члены данных класса, не вынуждая пользователей класса перекомпилировать их код. Это может быть приемлемо при некоторых обстоятельствах, но не, например, для системных библиотек.
Чтобы подробно объяснить проблему, рассмотрите следующий код в вашей общей библиотеке / заголовке:
// header
struct A
{
public:
A();
// more public interface, some of which uses the int below
private:
int a;
};
// library
A::A()
: a(0)
{}
Компилятор генерирует код в разделяемой библиотеке, который вычисляет адрес целого числа, которое должно быть инициализировано, чтобы быть определенным смещением (вероятно, ноль в этом случае, потому что это единственный член) от указателя на объект A, который он знает как this
.
На стороне пользователя кода new A
сначала выделит sizeof(A)
байт памяти, а затем передаст указатель на эту память конструктору A::A()
как this
.
Если в более поздней версии вашей библиотеки вы решите отбросить целое число, увеличить его, уменьшить или добавить члены, то будет несоответствие между количеством выделяемого пользователем кода памяти и смещениями, ожидаемыми кодом конструктора. , Вероятный результат - сбой, если вам повезет - если вам повезет меньше, ваше программное обеспечение ведет себя странно.
С помощью pimpl'ing вы можете безопасно добавлять и удалять члены данных во внутреннем классе, так как выделение памяти и вызов конструктора происходят в общей библиотеке:
// header
struct A
{
public:
A();
// more public interface, all of which delegates to the impl
private:
void * impl;
};
// library
A::A()
: impl(new A_impl())
{}
Все, что вам нужно сейчас сделать, - это освободить свой открытый интерфейс от элементов данных, кроме указателя на объект реализации, и вы защищены от этого класса ошибок.
Редактировать: Возможно, мне следует добавить, что единственная причина, по которой я говорю о конструкторе, заключается в том, что я не хотел предоставлять больше кода - такая же аргументация применима ко всем функциям, которые обращаются к членам данных .