Куб 0
В сфере программистов C++ существует устоявшийся термин "подсчет ссылок" (reference counting), обозначающий метод работы с объектами с использованием общих данных. Как только у данных появляется новый клиент, счетчик ссылок увеличивается на единицу, как только клиент завершает работу с данными, счетчик уменьшается на единицу. Когда последний клиент завершает работу с данными, данные уничтожаются. Зачем это нужно? Предположим, в Вашем проекте нужно обработать звуковые данные разными способами и затем сравнить результаты. Каждая процедура обработки считывает данные и приступает к работе. Если данные громоздки, разумно предоставить каждому клиенту ссылку на одни и те же данные, чтобы избежать их ненужного дублирования, но при этом вовсе не нужно давать понять клиентам, что они разделяют одни и те же данные. В идеальном случае клиент получает указатель на общие данные, и начинает работать с ним как с обычным указателем на объект, а в конце работы вызывает деструктор.
Ниже предлагается шаблонный класс RefCounter, реализующий механизм подсчета ссылок. Его схема функционирования схожа с std::auto_ptr. Объект RefCounter ведет себя как указатель на класс, специализировавший шаблон, и позволяет выполнять операции присваивания и вызовы методов специализирующего класса.

//------------------------------------------------------------
template <class T>
class RefCounter {
public:
RefCounter(): m_RC(new IntRep<T>(0)) {}
RefCounter(const RefCounter &A): m_RC(A.m_RC) {
++m_RC->m_RefCount;
}
RefCounter(T *Ptr): m_RC(new IntRep<T>(Ptr)) {}
RefCounter &operator= (const RefCounter &A) {
if (m_RC==A.m_RC) {
return *this;
}
if (--m_RC->m_RefCount==0) {
delete m_RC;
}
m_RC=A.m_RC;
++m_RC->m_RefCount;
return *this;
}
RefCounter &operator= (T *Ptr) {
if (--m_RC->m_RefCount==0) {
delete m_RC;
}
m_RC=new IntRep<T>(Ptr);
return *this;
}
const T *operator-> () const {
return m_RC->m_Data;
}
const T &operator* () const {
return *(m_RC->m_Data);
}
~RefCounter() {
if (--m_RC->m_RefCount==0) {
delete m_RC;
}
}
T *DeepCopy() const {
return new T(*m_RC->m_Data);
}
private:
template <class T>
struct IntRep {
IntRep(T *Ptr): m_RefCount(1), m_Data(Ptr) {}
~IntRep() {
delete m_Data;
}
int m_RefCount;
T *m_Data;
};
IntRep<T> *m_RC;
};
//------------------------------------------------------------
Важный аспект при использовании метода подсчета ссылок — неизменность разделяемых данных. Если один из клиентов желает изменить данные, тогда он должен получить себе локальную копию, и работать с ней, не мешая остальным. Для этого предусмотрен метод DeepCopy(). Так же стоит обратить внимание, что operator->() и operator*() сделаны константными, чтобы предупредить возможный вызов методов, изменяющих состояние объекта. Константность, конечно, не универсальное решение, зато простое, а статью растягивать на несколько страниц не хочется.
Примерное использование класса RefCounter показано далее.

//------------------------------------------------------------
class SomeClass {
public:
SomeClass(int Num): m_Num(Num) {}
void Method1() const {} // эти методы
void Method2() const {} // не изменят объект
private:
int m_Num; // эти данные будут общими
};
//------------------------------------------------------------
int main() {
// используем подсчет ссылок для int*
RefCounter<int> s;
s=new int(5);
RefCounter<int> s1;
s1=s; // разделение данных между двумя клиентами
int num1=*s.DeepCopy(); // так нельзя, ведет к утечке памяти
int num2=*s1; // вот так безопасно
// используем подсчет ссылок для SomeClass*
const RefCounter<SomeClass> rc1=new SomeClass(12);
RefCounter<SomeClass> rc2;
rc2=rc1; // разделение данных между двумя клиентами
rc2->Method1(); // примеры вызова метода класса SomeClass
rc1->Method2();
(*rc2).Method1(); // разыменовывание и вызов
RefCounter<SomeClass> rc3;
rc3=rc1.DeepCopy(); // создание полной копии
return 0;
}
//------------------------------------------------------------

@темы: Счётчики объектов