Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP.doc
Скачиваний:
7
Добавлен:
25.04.2019
Размер:
1.34 Mб
Скачать

8.4. Внедрение подсчета ссылок в существующий класс

Выше, при внедрении подсчета ссылок в класс, у нас был доступ к файлу с определением этого класса, но как включить подсчет ссылок в класс, который мы не можем изменять (например, класс из библиотеки)? Хороший вариант – создать собственный класс в качестве «обертки» (wrapper) для этого класса и внедрить подсчет ссылок в новый класс. Этот подход является весьма гибким и характеризуется прозрачностью для пользователя.

Ниже на примере показано, как внедрить подсчет ссылок в существующий класс на основе описанного подхода. Исходный класс описывает векторы с компонентами логического типа. Подсчет ссылок инкапсулируется в классе RefCounter, который мы уже видели выше.

Вот определение исходного класса векторов (возможно, в чем-то оно не эффективно, но его достаточно для нашей задачи):

class CBitVector {

public:

// конструктор

explicit CBitVector(size_t len):

_len(len > 0 ? len : 1), _items(new bool[_len]) { ; }

// копирующий конструктор

CBitVector(const CBitVector &);

// деструктор

virtual ~CBitVector();

// присваивание

CBitVector & operator = (const CBitVector &);

// ввод вектора из потока

friend istream & operator >> (istream &, CBitVector &);

// вывод вектора в поток

friend ostream & operator << (ostream &, const CBitVector &);

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

bool & operator [] (size_t);

bool operator [] (size_t) const;

// длина

size_t GetLength() const { return _len; }

protected:

size_t _len; // длина

bool * _items; // элементы

}; /* CBitVector */

...

// реализация функций принципиального значения не имеет

...

Вот определение нашего класса-обертки:

class CBitVectorW {

public:

// конструктор

CBitVectorW(size_t len):

__pvaluesimulator(new CValueSimulator(len)) { ; }

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

CBitVectorW(const CBitVectorW &);

// деструктор

~CBitVectorW();

// присваивание

CBitVectorW & operator = (const CBitVectorW &);

// ввод-вывод

friend istream & operator >> (istream &, CBitVectorW &);

friend ostream & operator << (ostream &, const CBitVectorW &);

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

bool & operator [] (size_t i);

bool operator [] (size_t i) const;

private:

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

class CValueSimulator: public CRefCounter {

public:

// конструктор

CValueSimulator(size_t len):

__prealvector(new CBitVector(len)) { ; }

CValueSimulator(const CValueSimulator & o):

__prealvector(new CBitVector(*o.__prealvector)) { ; }

~CValueSimulator() { delete __prealvector; }

// связь с «реальным» вектором

CBitVector * __prealvector;

}; /* CValueSimulator */

CValueSimulator * __pvaluesimulator; // связь со значением

// копирование (для устранения дублирования кода)

void __MakeCopy(const CBitVectorW &);

}; /* CBitVectorW */

// ----- реализация функций -----

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

inline bool CBitVectorW::operator [] (size_t i) const {

// проверка индекса

i = i >= this->__pvaluesimulator->__prealvector->GetLength() ? 0 : i;

// доступ к «реальному» вектору

return (*this->__pvaluesimulator->__prealvector)[i];

}

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

inline CBitVectorW::CBitVectorW(const CBitVectorW & o) {

__MakeCopy(o);

}

// деструктор

inline CBitVectorW::~CBitVectorW() {

this->__pvaluesimulator->RemoveRef();

}

// вывод

inline ostream & operator << (ostream & str, const CBitVectorW & o) {

// выводится замещаемый вектор

return str << *o.__pvaluesimulator->__prealvector;

}

// ввод

istream & operator >> (istream & str, CBitVectorW & o) {

if (o.__pvaluesimulator -> IsShared()) {

// попытка изменить разделяемое значение

o.__pvaluesimulator -> RemoveRef();

// создаем копию значения

o.__pvaluesimulator =

new CBitVectorW::CValueSimulator(*o.__pvaluesimulator);

}

return str >> *o.__pvaluesimulator->__prealvector;

}

// копирование

void CBitVectorW::__MakeCopy(const CBitVectorW & o) {

if (o.__pvaluesimulator -> CanBeShared() == false)

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

this->__pvaluesimulator =

new CBitVectorW::CValueSimulator(*o.__pvaluesimulator);

}

else {

// значение можно использовать совместно

this -> __pvaluesimulator = o.__pvaluesimulator;

this -> __pvaluesimulator -> AddRef();

}

}

// присваивание

CBitVectorW & CBitVectorW::operator = (const CBitVectorW & o) {

if (this == &o) return *this;

this -> __pvaluesimulator -> RemoveRef();

__MakeCopy(o);

return *this;

}

// индексация (для не-const-объекта)

bool & CBitVectorW::operator [] (size_t i) {

if ( i >= this->__pvaluesimulator->__prealvector->GetLength() )

throw std::range_error("Incorrect index");

if ( this -> __pvaluesimulator -> IsShared() ) {

// для разделяемого значения создаем копию

this -> __pvaluesimulator -> RemoveRef();

this -> __pvaluesimulator =

new CBitVectorW::CValueSimulator(*(*this).__pvaluesimulator);

}

// запрещаем совместное использование

this -> __pvaluesimulator -> MakeUnshareable();

// обращаемся к реальному вектору

return (*this->__pvaluesimulator->__prealvector)[i];

}

Ниже показано применение класса-обертки в программе. Из приведенного фрагмента кода видно, что работа с оберткой столь же удобна, как и использование «реального» класса, однако теперь у нас есть новая функциональность:

...

CBitVectorW v(5), w(4);

cin >> v >> w;

CBitVectorW z(v); // z и v разделяют значение

cout << endl << "S T E P - 1: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

w = v; // теперь z, v, w разделяют одно значение

cout << endl << "S T E P - 2: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

cin >> v; // для v создается копия

cout << endl << "S T E P - 3: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

w = v = z; // z, v, w снова разделяют одно значение

cout << endl << "S T E P - 4: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

w[0] = !w[0]; // w теперь имеет неразделяемое значение

cout << endl << "S T E P - 5: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

bool * pbool = &v[0]; // и v имеет неразделяемое значение

*pbool = !*pbool;

cout << endl << "S T E P - 6: " << endl;

cout << "v = " << v << "w = " << w << "z = " << z;

...

Из кода и комментариев легко понять принцип включения подсчета ссылок в недоступный для изменения класс и применять его в общем случае. Более подробная информация о механизме подсчета ссылок содержится в книге [6].

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]