- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •CPoint point1(100,70); // локальный объект
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
8. Подсчет ссылок
Подсчет ссылок является фундаментальным методом, который широко используется в рамках объектной технологии42. Он определяет правила, согласно которым несколько объектов с одинаковым значением работают с общей для них копией этого значения. Подсчет ссылок дает два ключевых преимущества. Во-первых, он позволяет уменьшить затраты памяти на хранение данных объектов. Во-вторых, он облегчает учет ресурсов динамической памяти при создании и удалении объектов.
8.1. Назначение механизма подсчета ссылок
Сущность метода подсчета ссылок заключается в отделении объекта от соответствующего ему значения. Объект содержит лишь ссылку (указатель) на свое значение, причем на это же значение могут ссылаться и другие объекты. Для учета числа объектов, имеющих некоторое значение V, с этим значением ассоциируется счетчик ссылок (reference count). Этот счетчик изменяется при каждом увеличении или уменьшении числа объектов со значением V; при добавлении объекта он увеличивается на единицу, а при удалении объекта – уменьшается на единицу. Когда значение V становится не нужным ни одному из объектов, его счетчик ссылок оказывается нулевым и оно безопасно удаляется из памяти. Объекты разделяют значение V до тех пор, пока один из них не «захочет» его изменить. В этом случае для такого объекта создается отдельная копия значения V, которая может быть модифицирована без влияния на значение других объектов, а счетчик ссылок значения V получает отрицательное единичное приращение. Такое копирование значения при попытке его изменения принято называть копированием при записи (copy on write).
8.2. Контекстно-независимая модель счетчика ссылок
Подсчет ссылок является контекстно-независимым методом, что позволяет инкапсулировать его в отдельном базовом классе (хотя применение отдельного класса вовсе не обязательно, оно способствует улучшению качества проекта, создавая благоприятные условия к повторному использованию кода и увеличивая уровень инкапсуляции). Подключая этот класс к любому пользовательскому классу, например, путем вложения или наследования, можно обеспечить поддержку подсчета ссылок для пользовательского класса. Класс подсчета ссылок можно применять и для модификации стандартных классов, если необходимо придать им функциональность подсчета ссылок.
Класс для подсчета ссылок (назовем его 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 абстрактный за счет чисто виртуального деструктора, поэтому объект – счетчик ссылок можно создать только как часть объекта производного класса, который использует возможности подсчета ссылок.
8.3. Внедрение подсчета ссылок в новый класс
Теперь воспользуемся введенным классом. Для этого определим какой-нибудь пользовательский класс и реализуем в нем подсчет ссылок из класса 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, представляющем строки символов. В принципе, подсчет ссылок разумно использовать тогда, когда есть вероятность, что много объектов класса будут иметь одинаковые значения, причем эти значения будут редко изменяться.
