Технология D-Pointer
Вступление
Термин d-pointer впервые ввел Arnt Gulbrandsen ( Trolltech ) для техники, которая обеспечивала бинарную совместимость библиотек. Библиотеки бинарно-совместимы, если приложение может работать с новой версией библиотеки без перекомпиляции самого приложения.
Описание технологии
Допустим, у нас есть библиотека с классом Person:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | Person.h class Person { public : void setName( const std::string &name); std::string name() const ; void setFam( const std::string &fam); std::string fam() const ; private : std::string name; std::string fam; }; |
Если мы решим в новой версии библиотеки добавить/удалить поле,
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | Person.h class Person { public : void setINN( const std::string &INN); std::string INN() const ; void setName( const std::string &name); std::string name() const ; void setFam( const std::string &fam); std::string fam() const ; private : std::string INN; std::string name; std::string fam; }; |
то приложение с новой версией библиотеки без перекомпиляции работать не будет. Это происходит потому что, доступ к полям класса осуществляется со смещением и добавление нового поля изменияет адресацию полей name и fam, а также из-за увеличения размера самого объекта.
Для решения данной проблемы была придумана технология d-pointer.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | Person.h class Person { public : Person(); ~Person(); void setName( const std::string &name); std::string name() const ; void setFam( const std::string &fam); std::string fam() const ; private : Person( const Person &person); const Person& operator=( const Person &person); struct PrivData; PrivData* const d; }; Person.cpp #include "Person.h" struct Person::PrivData { std::string name; std::string fam; }; Person::Person(): d( new PrivData()) { } Person::~Person() { delete d; } void Person::setName( const std::string &name) { d->name = name; } std::string Person::name() const { return d->name; } void Person::setFam( const std::string &fam) { d->fam = fam; } std::string Person::fam() const { return d->fam; } |
Теперь размер объекта на стеке будет равен 4 байта(для 32-x битной системы) и добавление новых полей не изменит его размера.
Хочу заметить, что если объект копируется, то мы должны добавить объявления конструктора копирования и оператора присваивания в заголовочный файл:
1 2 | Person( const Person &person); const Person &operator=( const Person &person); |
Также их определения в срр файле:
01 02 03 04 05 06 07 08 09 10 11 12 | Person::Person( const Person &person): d( new PrivData(*person.d)) { } const Person& Person::operator =( const Person &person) { if (&person != this ) *d = *person.d; return * this ; } |
Помимо бинарной совместимости технология d-pointer имеет и другие преимущества:
- Скрывает реализацию в cpp файле.
- Заголовочный файл занимает меньше размера и выглядит более аккуратно.
- Из-за меньшего размера заголовочных файлов, быстрей происходит компиляция приложения.
Заключение
Данная технология широко используется в таких проектах как Qt и KDE. В проекте KDE бинарная совместимость обеспечивается на протяжении старшего номера версии.
Для проверки библиотек, написанных на C/C++ есть скрипт, написанный на perl.
О изменениях, которые могут поломать совместимость можно почитать тут.