Есть очень реальная проблема с разделяемыми библиотеками, которую идиома 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
.
На стороне пользователя кода a 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())
{}
Все, что вам нужно сделать сейчас, это освободить ваш открытый интерфейс от членов данных, кроме указателя на объект реализации, и вы будете защищены от этого класса ошибок.
Изменить: мне, возможно, следует добавить, что единственная причина, по которой я говорю здесь о конструкторе, заключается в том, что я не хотел предоставлять больше кода - та же аргументация применяется ко всем функциям, которые обращаются к членам данных.