Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / LECS15.DOC
Скачиваний:
43
Добавлен:
16.04.2013
Размер:
114.18 Кб
Скачать

Дружественные классы и итераторы

Класс может определять объекты, которые необходимы в качестве внутренних деталей для других классов. Чтобы сохранить свою анонимную роль, такой класс должен быть закрытым. Класс, управляющий этими закрытыми объектами, должен быть в дружественных отношениях с ними.

Вспомним тип my_string, определенный с семантикой подсчета ссылок из раздела 6.10, «Строки, использующие семантику ссылок», на стр. 197. Отдельные значения my_string были в конечном счете ссылочными объектами типа str_obj. Это разъединение позволяло одному экземпляру str_obj, которому, возможно, требовалось много байт для хранения, быть использованным многими экземплярами my_string. Можно передать этот пример, используя дружественные отношения для сохранения закрытости класса str_obj.

В файле string8.H

class str_obj {

private:

friend class my_string;

friend class string_iterator;

friend ostream&

operator << (ostream& out, const my_string& str);

int len, ref_cnt;

char* s;

str_obj ( ) : len (0), ref_cnt (1) { s = new char [1];

str_obj (int n) : len (n), ref_cnt (1)

{ s = new char [n + 1]; }

str_obj (const char* p) : ref_cnt (1)

{ len = strlen (p); s = new char [len + 1];

strcpy (s, p); }

~ str_obj ( ) { delete [ ] s; }

};

Этот проект с использованием двух классов имеет преимущество – большую гибкость за счет дальнейшего разделения деталей реализации и клиентского кода.

Итератором также обычно необходимы дружественные отношения с объектом, который они перебирают. добавим класс-итератор и изменим наш my_string, чтобы перегрузить операторы присваивания и «помещения в», расширив таким образом наш пример.

В файле string8.H

class my_string {

public:

my_string ( ) { st = new str_obj; }

my_string ( int n ) { st = new str_obj (n); }

my_string (const char* p) { st = new str_obj (p); }

my_string (const my_string& str)

{ st = str . st; st -> ref_cnt++; }

~ my_string ( );

void assign (const my_string& str);

void print ( ) const { cout << st -> s ; }

my_string& operator= (my_string& str)

{ assign (str); return str; }

friend class string_iterator;

friend ostream&

operator << (ostream& out, const my_string& str);

private:

str_obj* st;

};

// дружественная функция класса my_string

// «поместить в» - типичный способ перегрузки <<

// Так как нужен синтаксис

// ostream_переменная << переменная_типа,

// функция-член класса my_string недопустима.

ostream& operator<< (ostream& out, const my_string& str)

{

out << str. st -> s;

return out;

}

Как было сказано, ostream& operator<< (ostream&, const my_string&) нуждается в дружественных отношениях с str_obj. Эта функция использует закрытые детали реализации класса my_string. Она не может быть записана как функция-член, так как ее первым аргументом должен быть ostream. Поскольку возвращаемое ею значение имеет тип ostream&, она может быть использована для многократной подстановки в выражение. Данное употребление соответствует соглашениям iostream.h.

Функция-член assign ( ) использовались нами для кодирования operator = ( ).

Чтобы позволить множественные присваивания возвращается ссылочное значение. Стоит отметить, что если бы присваивание не было перегружено, обычная семантика присвоения не работала бы – подсчет ссылок не выполнялся бы надлежащим образом.

Соответствующий класс-итератор следует общей схеме. у нас есть конструктор, связывающий объект итератора с перебираемым объектом с помощью инициализации my_string* ptr_s. Мы предусматриваем закрытую позиционную переменную cur_ind. Поскольку дружественные отношения между my_string и str_obj не транзитивны, для нашей схемы необходимо, чтобы и string_iterator был дружественен str-obj.

Соседние файлы в папке Лекции