Технология D-Pointer
Вступление
Термин d-pointer впервые ввел Arnt Gulbrandsen ( Trolltech ) для техники, которая обеспечивала бинарную совместимость библиотек. Библиотеки бинарно-совместимы, если приложение может работать с новой версией библиотеки без перекомпиляции самого приложения.
Описание технологии
Допустим, у нас есть библиотека с классом Person:
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; };
Если мы решим в новой версии библиотеки добавить/удалить поле,
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.
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 битной системы) и добавление новых полей не изменит его размера.
Хочу заметить, что если объект копируется, то мы должны добавить объявления конструктора копирования и оператора присваивания в заголовочный файл:
Person(const Person &person); const Person &operator=(const Person &person);
Также их определения в срр файле:
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.
О изменениях, которые могут поломать совместимость можно почитать тут.