Технология D-Pointer

10 октября 2011 г.

Вступление

Термин 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.

О изменениях, которые могут поломать совместимость можно почитать тут.

Теги: рубрика Интернет