Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Назначение механизма подсчета ссылок

Поиск

Сущность метода подсчета ссылок заключается в отделении объекта от соответствующего ему значения. Объект содержит лишь ссылку (указатель) на свое значение, причем на это же значение могут ссылаться и другие объекты. Для учета числа объектов, имеющих некоторое значение V, с этим значением ассоциируется счетчик ссылок (reference count). Этот счетчик изменяется при каждом увеличении или уменьшении числа объектов со значением V; при добавлении объекта он увеличивается на единицу, а при удалении объекта – уменьшается на единицу. Когда значение V становится не нужным ни одному из объектов, его счетчик ссылок оказывается нулевым и оно безопасно удаляется из памяти. Объекты разделяют значение V до тех пор, пока один из них не «захочет» его изменить. В этом случае для такого объекта создается отдельная копия значения V, которая может быть модифицирована без влияния на значение других объектов, а счетчик ссылок значения V получает отрицательное единичное приращение. Такое копирование значения при попытке его изменения принято называть копированием при записи (copy on write).


Контекстно-независимая модель счетчика ссылок

Подсчет ссылок является контекстно-независимым методом, что позволяет инкапсулировать его в отдельном базовом классе (хотя применение отдельного класса вовсе не обязательно, оно способствует улучшению качества проекта, создавая благоприятные условия к повторному использованию кода и увеличивая уровень инкапсуляции). Подключая этот класс к любому пользовательскому классу, например, путем вложения или наследования, можно обеспечить поддержку подсчета ссылок для пользовательского класса. Класс подсчета ссылок можно применять и для модификации стандартных классов, если необходимо придать им функциональность подсчета ссылок.

Класс для подсчета ссылок (назовем его RefCounter) должен инкапсулировать следующие возможности: а) хранение значения счетчика ссылок; б) увеличение и уменьшение счетчика ссылок; в) удаление значения объекта при нулевом счетчике ссылок; г) управление разрешением на совместное использование значения объекта. Вариант определения класса RefCounter имеет вид:

class RefCounter {

public:

RefCounter();

RefCounter(const RefCounter &);

RefCounter & operator = (const RefCounter &);

virtual ~RefCounter() = 0;

Void AddRef(); // добавить ссылку

Void RemoveRef(); // убрать ссылку

Bool isShareable() const; // проверка возможности совместного доступа

Bool isShared() const; // проверка совместного доступа

Void MakeUnshareable(); // запретить совместный доступ

private:

size_t _refcount; // счетчик ссылок

bool _shareable; // признак разрешения совместного доступа

};

Реализация этого класса будет следующей:

RefCounter::RefCounter(): _refcount(0), _shareable(true) {}

RefCounter::RefCounter(const RefCounter &):

_refcount(0),

_shareable(true)

{}

RefCounter & RefCounter::operator = (const RefCounter &)

{ return *this; }

RefCounter::~RefCounter() {}

void RefCounter::AddRef() { _refcount++; }

void RefCounter::RemoveRef() { if(--_refcount == 0) delete this; }

bool RefCounter::isShareable() const { return _shareable; }

bool RefCounter::isShared() const { return _refcount > 1; }

void RefCounter::MakeUnshareable() { _shareable = false; }

Функции класса RefCounter выглядят обычно, за исключением, пожалуй, конструкторов и операции присваивания. В обоих конструкторах начальное значение счетчика ссылок равно нулю. Создатели объектов класса RefCounter должны самостоятельно устанавливать его в единицу (в конечном счете это ведет к упрощению кода). Операция присваивания вполне законно не делает никакой работы, поскольку объекты – счетчики ссылок не копируются; вместо них копируется значение, с которым они ассоциированы. Класс RefCounter абстрактный за счет чисто виртуального деструктора, поэтому объект – счетчик ссылок можно создать только как часть объекта производного класса, который использует возможности подсчета ссылок.


Внедрение подсчета ссылок в новый класс

Теперь воспользуемся введенным классом. Для этого определим какой-нибудь пользовательский класс и реализуем в нем подсчет ссылок из класса RefCounter. Пусть таким классом будет класс поименованных одномерных массивов с произвольным диапазоном индексов.

Определение класса имеет вид:

class NamedArray {

public:

NamedArray(double * items, int lbound, int hbound,

const string & name);

NamedArray(const NamedArray & sample);

const double & operator [] (int i) const;

double & operator [] (int i);

friend ostream & operator << (ostream & os, const NamedArray & array);

~NamedArray();

private:

// вложенный класс – значение массива со счетчиком ссылок

struct ArrayValue: public RefCounter {

ArrayValue(double * items, int lbound, int hbound,

const string & name);

ArrayValue(const ArrayValue & value);

~ArrayValue() { delete[] _items; }

double * _items; // элементы массива

int _lbound; // нижний индекс

int _hbound; // верхний индекс

string _name; // имя массива

};

// указатель, связывающий объект со значением и счетчиком ссылок

ArrayValue * _pValue;

NamedArray(); // конструктор по умолчанию делаем недоступным

};

Реализация функций класса NamedArray следующая:

// конструктор значения массива

NamedArray::ArrayValue::ArrayValue(double * items,

int lbound, int hbound, const string & name)

{

size_t size = hbound - lbound + 1;

_items = new double [size];

for(size_t i=0; i<size; ++i) _items[i] = items[i];

_lbound = lbound;

_hbound = hbound;

_name = name;

}

// конструктор копирования значения

NamedArray::ArrayValue::ArrayValue(const ArrayValue & value)

{

size_t size = value._hbound - value._lbound + 1;

_items = new double [size];

for(size_t i=0; i<size; ++i) _items[i] = value._items[i];

_lbound = value._lbound; _hbound = value._hbound;

_name = value._name;

}

// конструктор массива

NamedArray::NamedArray(double * items,

int lbound, int hbound, const string & name):

_pValue(new ArrayValue(items,lbound,hbound,name))

{ }

// конструктор копирования массива

NamedArray::NamedArray(const NamedArray & sample)

{

if(sample._pValue->isShareable())

{

this->_pValue = sample._pValue;

_pValue->AddRef();

}

else _pValue = new ArrayValue(*sample._pValue);

}

// деструктор массива

NamedArray::~NamedArray() { delete _pValue; }

// индексация

const double & NamedArray::operator [] (int i) const

{

return _pValue->_items[i - _pValue->_lbound];

}

double & NamedArray::operator [] (int i)

{

if(_pValue->isShared()) {

_pValue->RemoveRef();

_pValue = new ArrayValue(_pValue->_items,

_pValue->_lbound, _pValue->_hbound, _pValue->_name);

}

_pValue->MakeUnshareable();

return _pValue->_items[i - _pValue->_lbound];

}

// вывод

ostream & operator << (ostream & os, const NamedArray & array)

{

for(int i=0; i<array._pValue->_hbound-array._pValue->_lbound+1; ++i)

os << array._pValue->_items[i] << " ";

return os;

}

Ниже приведен пример использования класса NamedArray в программе.

Пример

double items[] = { 0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0 };

// создание исходного массива

NamedArray a1(items, 1, 10, "array");

// создание копии массива

// здесь значение не копируется, а увеличивается счетчик ссылок

NamedArray a2(a1);

// здесь тоже значение не копируется, увеличивается счетчик ссылок

NamedArray a3 = a2;

cout << endl << "Array1 = " << a1; // 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

cout << endl << "Array2 = " << a2; // 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

cout << endl << "Array3 = " << a3; // 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

// здесь необходимо скопировать значение, так как оно меняется

a3[1] = 9.99;

// здесь создается копия объекта и копия значения, так как

// значение a3 больше нельзя использовать совместно

NamedArray a4(a3);

cout << endl << "Array1 = " << a1; // 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

cout << endl << "Array2 = " << a2; // 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

cout << endl << "Array3 = " << a3; // 9.99 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

cout << endl << "Array4 = " << a4; // 9.99 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Реализацию подсчета ссылок и копирование при записи аналогично можно обеспечить в любом другом пользовательском классе, где объекты могут иметь совпадающие значения. Эти механизмы поддерживаются также во многих стандартных классах, например, в шаблонном классе basic_string, представляющем строки символов. В принципе, подсчет ссылок разумно использовать тогда, когда есть вероятность, что много объектов класса будут иметь одинаковые значения, причем эти значения будут редко изменяться.




Поделиться:


Последнее изменение этой страницы: 2016-08-14; просмотров: 179; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 52.15.211.173 (0.009 с.)