Pull to refresh

Технология 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.

О изменениях, которые могут поломать совместимость можно почитать тут.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.