Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ТП_ЛабРаботы.doc
Скачиваний:
13
Добавлен:
28.09.2019
Размер:
716.8 Кб
Скачать

Friend-конструкции

Private-данные и методы какого-либо класса прямо доступны только внутри методов класса. Это требует обеспечить каждый класс достаточным количеством методов для манипуляции данными во всех реально встречаемых ситуациях. C++ имеет способ обойти это жесткое ограничение и позволить избранным внешним классам, отдельным ме­тодам класса или обычным внешним функциям прямой доступ ко всем данным класса, в том числе и к private. Самое простое — это объявить обычную функ­цию (не являющуюся членом какого-либо класса) friend-функцией класса. Те­перь она имеет доступ ко всем данным и методам класса и может модифициро­вать private-данные класса. Например:

class A {

private:

int i; // private-данное

friend void set (A*, int); // friend-функция

public:

void set (int j) { i=j; } // public-метод

void p() { printf("\n i=%d”,i);}

};

void set (A* p, int i) // Тело friend-функции

{

p->i=i;

}

void main()

{

A obj; // Объект obj класса А

set (&obj, 10); //Доступ с помощью friend-функции

obj.p(); // Вывод поля

obj.set (11); // Доступ с помощью public-метода

obj.p();

}

В примере объявлена внешняя функция set. Она не принадлежит никакому классу. Но она, будучи объявленной friend-функцией класса А, имеет прямой до­ступ к private-переменной класса А, так же, как и метод set собственного класса. Указатель (или ссылку) на объект класса А следует передать в качестве парамет­ра, так как friend-функция является посторонней функцией и не знает объекта obj класса А (в отличие от метода void A::set(int); класса, которому неявно передается указатель this). При вызове внешней функции мы передаем ей &obj — адрес объекта. Этот адрес присваивается локальному указателю р, после чего вы­ражение p->i позволяет обратиться к private-переменной объекта, на который ссылается указатель р. Поле объекта obj прямо доступно внутри friend-функции set.

Friend-функция может принадлежать какому-либо другому классу. Тогда ее мож­но назвать friend-методом. Чтобы сократить время доступа метода класса В к методу класса А, целесообразно объявить в классе А конкретный метод класса В как friend-метод класса А. Кроме того, в классе А можно объявить весь класс В как friend-клacc класса А. Теперь все методы класса В являются friend-методами класса А, то есть внутренние данные класса А теперь прямо доступны в методах класса В. Объявление friend-конструкций может быть размещено в любой секции (private, protected, public) описания класса. Это не влияет на смысл объявления. Следующая схема иллюстрирует рассмотренные возможности:

class A{

friend int strange(char* ch); // Friend-функция

friend class B; // Friend-класс

friend char* C::methc(); // Friend-метод класса С

};

Свойство friend не транзитивно, то есть если класс В объявлен friend-клас­сом в классе А, а класс С — в классе В, то это не означает, что класс С является friend-классом для класса А. Свой­ство friend не наследуется.

Функции ввода-вывода библиотеки классов C++

Процедуры ввода-вывода библиотеки, на кото­рую ссылается iostream.h, осуществляют преобразование значений переменные стандартных типов (int, double и т.п.) в последовательность символов, и наоборот. В библиотеке описан класс ostream, в котором операция << переопре­делена так, чтобы вывод в поток данных всех стандартных типов языка C++ был максимально простым и удобным. Операция >> переопределена в классе istream для ввода данных стандартных типов. Для ввода и вывода внутренних данных классов, опре­деленных пользователем, операции >> и << должны быть переопределены в этих клас­сах.

В библиотеке определены объекты класса ostream для организации операций ввода/вывода:

  • cout - связан со стандартным потоком вывода и является аналогом потока stdout в библиотеке stdio.h;

  • сerr -поток вывода сообщений об ошибках ввода-вывода;

  • clog - поток вывода сообще­ний пользователю;

  • endl – заканчивает строку вывода и очищает буфер (переход на новую строку).

Объект cin класса istream обозначает стандартный поток ввода.

Пример иллюстрирует использование операций >> и << для ввода и вы­вода стандартных типов.

#include <iostream.h>

void main()

{

char с = 'С', str[80];

int i = -32767;

float v = 2.99792e8, t=3, s=0.;

//= = = = = = = Вывод в поток cout = = = ====//

cout <<\n i=" << i;

cout << "\n c=" << c;

cout << "\n s+vt= " << (s+v*t) << " m";

cout << "\nEnter an integer : "; cin >> i;

cout << "\nA real : "; cin >> s;

cout << "\nA string of chars: "; cin >> str;

cout << "\nYou entered: "<<i<<' '<<s<<' '<<str<<endl;

}

Результат работы этой программы:

i=-32768

с=С

s+vt= 8.99376e+08 m

Для форматирования текста в потоке вывода используются функции-манипуляторы:

  • setw(n) – устанавливает ширину поля, равной n-символам

cout << "d1 = " << setw(20) << d1 << endl;

  • setprecision(n) – устанавливает количество выводимых цифр для вещественных чисел

cout << " v=" << setprecision(3)<<v;

  • setioflags(flag1 | flag2 | …) – устанавливает флаги форматирования выходного потока

cout << setiosflags(ios_base::scientific);

Значения флагов, а также описание действия манипуляторов resetioflags, setfill, setbase смотрите в MSDN Library.

Переопределение операций

С помощью ключевого слова operator программист для объектов класса может переопределить смысл любой из более чем 40 встроенных операций языка (*, /, +, -, &&, и т.д.). Существуют два способа переопределения бинарных операций: public-методом с одним аргументом и friend-функцией с двумя аргу­ментами. Унарную операцию можно переопределить либо public-методом класса без параметров, либо friend-функ­цией с одним параметром.

Рассмотрим выражение х+у, где х, у являются объектами какого-либо класса. Предположим, что в этом классе одним из двух возможных способов переопределена бинарная операция +. Тогда выражение х+у может быть интерпретировано компилятором также двумя способами:

х.operator + (у); //либо как

operator + (х,у);

Первая интерпретация означает, что объекту х посылается сообщение operator+(у). То есть вызывается функция (член класса) с именем operator+(), при этом в качестве единственного параметра передается объект у. Вторая означает вызов внешней функции с именем operator+(), которой передаются два параметра х и у.

Функция, переопре­деляющая операцию, не может изменить количество аргументов операции. На­пример, унарная операция не может стать бинарной, и наоборот. Невозможно также изменить существующий приоритет при нормальном использовании операции.

Переопределяя операции, мы как бы наделяем язык свойствами, которыми он раньше не обладал. Например, переопределив операции +,-,*,/ в классе комплексных чисел, мы как бы наделяем язык C++ способностью оперировать комплексными числами по обычным правилам.

Оператор cout<<i; интер­претируется как cout.operator<<(i);. Так как тип возвращаемого значения операции - ostream&, то возможна цепоч­ка последовательного вывода. Оператор cout<<i<<j; интерпретируется как (cout.operator<<(i)).operator<<(j);.

Для ввода и вывода переменных нестандартных типов, например, объектов класса, операции << и >> следует переопределить в этом классе с помощью friend-функций.

class Vector {

double x1, x2; // Координаты вектора

public:

// Три конструктора

Vector (double c1, double c2) // С параметрами

{x1=c1; x2=c2;}

Vector() // По умолчанию

{x1=x2= 0.; }

Vector (const Vector& v) // Копирования

{x1=v.x1; x2=v.x2;}

//= = = = = = Переопределение операций =====//

friend ostream& operator<<(ostream&, Vector& v);

friend istream& operator>>(istream&, Vector& v);

};

Pеали­зуем тела friend-функций вне блока описания класса.

ostream& operator<<(ostream& os, Vector& v)

{return os<<"\n Vector: x1="<<v.x1<<" x2="<<v.x2;}

istream& operator>>(istream& is, Vector& v)

{

double d1, d2;

cout << "\n x1= "; is >> d1;

cout << " x2= "; is >> d2;

v=Vector (d1, d2); // Создание объекта

return is; // Входной поток

}

Первый формальный параметр (&os) функции operator<< — ссылка на объект класса ostream. С ним связан стандартный поток cout. Второй параметр является ссылкой на объект класса Vector. Функция возвращает адрес выходного потока. Аналогично для переопределенной операции вво­да. Теперь можно вводить и выводить объекты класса Vector:

Vector х, у;

//Переопределенные операции ввода-вывода cout << "\n\n Enter vector:"; cin >> x; cout << "\n Enter one more:"; cin >> y; cout << "\n\n You have entered: " << x <<y;