Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Назначение механизма подсчета ссылокСодержание книги
Поиск на нашем сайте
Сущность метода подсчета ссылок заключается в отделении объекта от соответствующего ему значения. Объект содержит лишь ссылку (указатель) на свое значение, причем на это же значение могут ссылаться и другие объекты. Для учета числа объектов, имеющих некоторое значение 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 с.) |