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

24.Дружественные функции. Присваивание объектов. Передача объектов функциям.

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

Функции-"друзья"

В C++ существует возможность разрешить доступ к закрытым членам класса функциям, которые не являются членами этого класса. Для этого достаточно объявить эти функции "дружественными" (или "друзьями") по отношению к рассматриваемому классу. Чтобы сделать функцию "другом" класса, включите ее прототип в public-раздел объявления класса и предварите его ключевым словом friend. Например, в этом фрагменте кода функция frnd() объявляется "другом" класса cl.

class cl {

// . . . public:

friend void frnd(cl ob) ;

Ключевое слово friend предоставляет функции, которая не является членом класса, доступ к его закрытым членам.

Как видите, ключевое слово friend предваряет остальную часть прототипа функции. Функция может быть "другом" нескольких классов.

Рассмотрим короткий пример, в котором функция-"друг" используется для доступа к закрытым членам класса myclass.

//294

// Демонстрация использования функции-"друга".

#include <conio.h>

#include <iostream.h>

class myclass { int a, b;

public:

myclass (int i, int j) { a=i; b=j; }

friend int sum(myclass x); // Функция sum() - "друг" класса myclass.

// Обратите внимание на то, что функция sum() не я

//вляется членом ни одного класса,

};

int sum(myclass x) {

/* Поскольку функция sum() -- "друг" класса myclass,

она имеет право на прямой доступ к его членам

данных а и b. */

return x.a + x.b; }

int main () { clrscr();

myclass n(3, 4); cout << sum(n);

getch(); return 0; }

В этом примере функция sum () не является членом класса myclass. Тем не менее она имеет полный доступ к private-членам класса myclass. В частности, она может непосредственно использовать значения х.а и х.b. Обратите также внимание на то, что функция sum() вызывается обычным образом, т.е. без привязки к объекту (и без использования оператора "точка"). Поскольку она не функция-член, то при вызове ее не нужно квалифицировать с указанием имени объекта. (Точнее, при ее вызове нельзя задазать имя объекта.) Обычно функции-"другу" в качестве параметра передается один или несколько объектов класса, для которого она является "другом", как в случае функции sum().

Несмотря на то что в данном примере мы не извлекаем никакой пользы из объявления функции sum() "другом", а не членом класса myclass, существуют определенные обстоятельства, при которых статус функции-"друга" имеет большое значение. Во-первых, функции-"друзья" могут быть полезны для перегрузки операторов определенных типов. Во-вторых, функции-"друзья" упрощают создание некоторых функций ввода-вывода. Об этом речь впереди.

Третья причина использования функций-"друзей" состоит в том, что в некоторых случаях два (или больше) класса могут содержать члены, которые находятся во взаимной связи с другими частями программы. Например, у нас есть два различных класса, которые при возникновении определенных событий отображают на экране "всплывающие" сообщения. Другие части программы, которые предназначены для вывода данных на экран, должны знать, является ли "всплывающее" сообщение активным, чтобы случайно не перезаписать его. В каждом классе можно создать функцию-член, возвращающую значение, по которому можно судить о том, активно сообщение или нет; однако проверка этого условия потребует дополнительных затрат (т.е. двух вызовов функций вместо одного). Если статус "всплывающего" сообщения необходимо проверять часто, эти дополнительные затраты могут оказаться попросту неприемлемыми. Однако с помощью функции, "дружественной" для обоих классов, можно напрямую проверять статус каждого объекта, вызывая только одну функцию, которая будет иметь доступ к обоим классам. В подобных ситуациях функция-"друг" позволяет написать более эффективный код. Эта идея иллюстрируется на примере следующей программы.

//295

// Использование функции-"друга".

#include <conio.h>

#include <iostream.h>

const int IDLE=0;

const int INUSE=1;

class C2; // опережающее объявление

class Cl {

int status; // IDLE, если сообщение неактивно,

// INUSE, если сообщение выведено на экран. // ...

public:

void set_status(int state);

friend int idle(Cl a, C2 b);

};

class C2 {

int status; // IDLE, если сообщение неактивно,

// INUSE, если сообщение выведено на экран.

// ...

public:

void set_status(int state);

friend int idle(Cl a, C2 b);

};

void Cl::set_status(int state) {status = state; }

void C2::set_status(int state) { status = state;}

// Функция idle() - "друг" для классов Cl и С2.

int idle(Cl a, C2 b) {

if(a.status || b.status) return 0; else return 1; }

int main() { clrscr();

Cl x; C2 y;

x.set_status(IDLE);

y.set_status(IDLE);

if(idle(x, y)) cout << "Экран свободен.\n";

else cout << "Отображается сообщение.\n";

x.set_status(INUSE);

if(idle(x, y)) cout <<"Экран свободен.\n";

else cout << "Отображается сообщение.\n";

getch(); return 0; }

При выполнении программа генерирует такие результаты.

Экран свободен.

Отображается сообщение.

Поскольку функция idle () является "другом" как для класса С1, так и для класса C2, она имеет доступ к закрытому члену status, определенному в обоих классах. Таким образом, состояние объекта каждого класса одновременно можно проверить всего одним обращением к функции idle ().

Опережающее объявление предназначено для объявления имени классового типа до определения самого класса.

Обратите внимание на то, что в этой программе используется опережающее объявление (также именуемое опережающей ссылкой) для класса С2. Его необходимость обусловлена тем, что объявление функции idle() в классе С1 использует ссылку на класс C2 до его объявления. Чтобы создать опережающее объявление для класса, достаточно использовать формат, представленный в этой программе.

"Друг" одного класса может быть членом другого класса. Перепишем предыдущую программу так, чтобы функция idle() стала членом класса С1. Обратите внимание на использование оператора разрешения области видимости (или оператора разрешения контекста) при объявлении функции idle () в качестве "друга" класса С2.

//297

/* Функция может быть членом одного класса и одновременно

"другом" другого. */

#include <conio.h>

#include <iostream.h>

const int IDLE=0; const int INUSE=1;

class C2; // опережающее объявление

class C1 {

int status; // IDLE, если сообщение неактивно,

// INUSE, если сообщение выведено на экран.

// ...

public:

void set_status(int state);

int idle(C2 b); // теперь это член класса Cl

};

class C2 {

int status; // IDLE, если сообщение неактивно,

// INUSE, если сообщение выведено на экран.

// ...

public:

void set_status(int state);

friend int C1::idle(C2 b); // функция-"друг"

};

void C1::set_status(int state) { status = state; }

void C2::set_status(int state) {status = state;}

// Функция idle() -- член класса С1 и "друг" класса С2.

int C1::idle(C2 b) { if (status || b.status) return 0; else return 1; }

int main() { clrscr();

C1 x;

C2 y;

x.set_status(IDLE);

y.set_status(IDLE);

if(x.idle(y)) cout << "Экран свободен.\n";

else cout << "Отображается сообщение.\n";

x.set_status(INUSE);

if (x.idle(y)) cout << "Экран свободен.\n";

else cout << "Отображается сообщение.\n";

getch(); return 0; }

Поскольку функция idle() является членом класса Cl, она имеет прямой доступ к переменной status объектов типа С1. Следовательно, в качестве параметра необходимо передавать функции idle() только объекты типа С2.

Передача объектов функциям

Объект можно передать функции точно так же, как значение любого другого типа данных. Объекты передаются функциям путем использования обычного С++-соглашения о передаче параметров по значению. Таким образом, функции передается не сам объект, а его копия. Следовательно, изменения, внесенные в объект при выполнении функции, не оказывают никакого влияния на объект, используемый в качестве аргумента для функ­ции. Этот механизм демонстрируется в следующей программе.

//303

//передача объектов в функцию

#include <conio.h>

#include <iostream.h>

class OBJ {

int i;

public:

void set_i(int x) { i = x; }

void out_i() { cout << i << " "; }

};

void f (OBJ x) {

x.out_i(); // Выводит число.

x.set_i(100); // Устанавливает только локальную копию.

x.out_i(); } // Выводит число 100.

int main() { clrscr();

OBJ o;

o.set_i(10) ;

f (o);

o.out_i(); // По-прежнему выводит число 10, значение

//переменной i не изменилось.

getch(); return 0; }

Вот как выглядят результаты выполнения этой программы.

10 100 10

Как подтверждают эти результаты, модификация объекта х в функции f() не влияет на объект о в функции main ().