В сфере программистов 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; } //------------------------------------------------------------
|