Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга C++.doc
Скачиваний:
24
Добавлен:
10.11.2019
Размер:
2.48 Mб
Скачать

Домашнее задание

Задания первого уровня:

  1. Создайте класс Date, который будет содержать информацию о дате (день, месяц, год). С помощью механизма перегрузки операторов, определите операцию сложения двух дат, а также операцию увеличения даты на определенное количество дней.

  2. Добавить в строковый класс функцию, которая создает строку, содержащую пересечение двух строк, то есть общие символы для двух строк. Например, результатом пересечения строк "sdqcg" "rgfas34" будет строка "sg". Для реализации функции перегрузить оператор * (бинарное умножение).

Дружественные функции (Friend Functions)

Итак, мы уже познакомились с одним из основных правил ООП: поля-данные (переменные) класса должны быть защищены от внешнего воздействия, то есть доступ к ним можно получить только с помощью функций-членов данного класса.

Тем не менее, как показывает практика, иногда необходимо получить прямой доступ к полям-данных класса, не используя его интерфейс. Т.е. чуть-чуть отступить от правил и обратиться к полям-данных класса через функцию или функцию-член другого класса.

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

Для решения подобного круга задач в С++ и существует возможность описания функции, функции-члена другого класса как дружественной (friend). Класс может предоставлять особые привилегии определенным внешним функциям или функциям-членам другого класса. Вот эти функции и получили название дружественных. Рассмотрим как работает этот механизм.

Для описания функции (функции-члена) дружественной тому или иному классу, необходимо в описании этого класса объявить дружественную функцию, используя ключевое слово friend. Причем, обратите внимание, не играет никакой роли в каком из разделов класса Вы разместите объявление дружественной функции.

Согласитесь, небольшие нарушения ограничения доступа могут быть весьма полезны, если их использовать с достаточной осторожностью. Словосочетание "с достаточной осторожностью" обозначает, что функция или класс, пользующиеся привилегиями доступа должны быть очень тесно связаны с тем классом, который наделил их этими привилегиями. Главной целью использования дружественных функций и классов является повышение эффективности программы. Дружественные функции и классы могут осуществлять прямой доступ к закрытым полям объекта без использования функций-членов этого класса. Рассмотрим пример использования дружественной функции (объяснение приводится в коментариях):

#include <iostream.h>

class A //интерфейс (протокол) класса A

{

//закрытая область данных

private: //хотя, по умолчанию, действует модификатор

//доступа private

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

int pole; //вот эта переменная и будет нашей подопытной

//открытая область данных

public:

A(){ //конструктор по умолчанию

pole=55;//инициализация закрытой переменной

//выведем на экран сообщение, что

//конструктор отработал

cout<<"Constructor A\n";

}

//объявление дружественной функции которая принимает экземпляр класса A

friend void ourfriend(const A &f);

};

//раз объявили дружественную функцию, то давайте уже и реализуем ее

void ourfriend(const A &f)

{

//итак, для демонстрации работы дружественной функции, мы обратимся

//к закрытой переменной pole экземпляра класса f и выведем ее

//значение на экран.

cout<<"\nWow! We have free access to private members,\n";

cout<<"pole ="<<f.pole<<endl;

}

void main()

{

A example; //создали экземпляр класса А - example

ourfriend(example); //собственно тестирование дружественной

//функции

}

Результат выполнения программы:

Constructor A

Wow! We have free access to private members,

pole =55

Итак, мы смогли получить доступ к закрытой переменной класса из функции (которая, при этом, не является членом класса), используя механизм дружественных функций. Заметим, что при работе с дружественной функцией следует помнить о том, что она не имеет указателя this класса в котором она объявлена как дружественная. Почему?.. Ответ мы найдем в самом смысле использования дружественных функций: дружественная функция не является членом этого класса.

Если все функции-члены одного класса являются дружественными функциями другого класса, то это можно записать как:

friend class имя_класса;

Может ли функция быть дружественной нескольким классам? Да, но если функция дружественна нескольким классам, то надо добавить это описание во все классы, к внутренним данным которых будет производиться обращение. Подозреваем, что у Вас возник вопрос: "А как же быть с защищенностью данных, если можно получить доступ к данным напрямую?" Не волнуйтесь, с точки зрения использования механизма инкапсуляции (напомним, под этим понятием мы подразумеваем сокрытие данных) защищенность данных сохраняется, поскольку полностью исключается всякая возможность доступа к данным так, чтобы объект не был осведомлен об этом. То есть в дружественных функциях мы свободно работаем только с экземплярами даного класса, но не с самим классом.

Снова, рассмотрим пример, но на этот раз, в роли дружественных функций у нас будут выступать функции-члены.

#include <iostream.h>

class A //интерфейс (протокол) класса A

{

//открытая область данных

public:

A(){ //конструктор по умолчанию

cout<<"Constructor A\n";

//конструктор отработал

}

//функция-член FriendToB() будет использоваться как

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

void FriendToB();

void SomeFunction();

};

class B //интерфейс (протокол) класса B

{

//область закрытых данных

private:

int i,j; //наши "подопытные" закрытые переменные;

//если сможем обратиться к этим переменным

//из дружественной функии FriendToB(), то

//"опыт удался"

//область открытых данных

public:

B(){

i=10; //произведем инициализацию

j=17;

cout<<"Constructor B\n";

//конструктор отработал

}

//укажем, что класс B имеет дружественную функцию, которая

//является функцией-членом класса A

friend void A::FriendToB();

};

class C //интерфейс (протокол) класса C

{

//область закрытых данных

private:

int k,n; //закрытые

//область открытых данных

public:

C(){

k=4; //начальная инициализация

n=7;

cout<<"\n\nConstructor C\n";

//конструктор отработал

}

//укажем что ВСЕ функции-члены класса A, являются

//дружественными для даного класса

friend class A;

};

//действительно ли может функция-член одного

//класса обращаться к закрытым данным другого

//класса. Давайте проверим!

void A::FriendToB()

{

B example; //объявим экземпляр класса B

cout<<"So, variables' value are:\n";

//и произведем доступ к закрытым данным

cout<<"example.i="<<example.i;

cout<<"\nexample.j="<<example.j<<endl;

}

//действительно ли после объявления в классе С

//friend class A; -все функции-члены класса A

//станут дружественными функциями для класса С?

//Ведь мы нигде не указывали, что функция-член

//SomeFunction() является дружественной классу С.

//Надо проверить...

void A::SomeFunction()

{

C temp; //объявим экземпляр класса С

cout<<"And now we are testing friend class...\n";

//и произведем доступ к закрытым данным

cout<<"temp.k="<<temp.k;

cout<<"\ntemp.n="<<temp.n<<endl;

}

void main()

{

A test; //создали экземпляр класса А - example

//собственно тестирование дружественной функции классу B

test.FriendToB();

//проверим, все ли функции-члены класса A являются дружественными

//классу С (на примере, функции SomeFunction)

test.SomeFunction();

}

Вот что будет выведено на экран, в результате выполнения программы:

Constructor A

Constructor B

So, variables' value are:

example.i=10

example.j=17

Constructor C

And now we are testing friend class...

temp.k=4

temp.n=7

Итак, если функция или функция-член объявлены как дружественные, то такие функции или функции-члены такого класса могут осуществлять непосредственный доступ ко всем (private, protected, public) данным (полям-данным и функциям-членам) класса, для которого они дружественны.

Теперь, когда Вы узнали о дружественных функциях, смело выбирайте ссылку ...И снова класс string, чтобы узнать об одной из чудесных возможностей использования дружественных функций - перегрузке операторов.