DOSUG CZ– розовая кнопка на сайте!
Logo

Друзья в С++
(Том Сван, "Освоение Borland C++ 4.5")

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

Замечание
Если друзья имеют брата-близнеца в C++, так это оператор goto. Подобно goto, друзья позволяют вам пренебречь правилами, призванными помочь вам написать надежный код. Не подумайте, что следующий раздел поощряет использоване друзей. Асы в программировании на С++ пользуются друзьями только в случае крайней необходимости.

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

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

class AClass {
private:
  double value; // Закрытый член класса
public:
  AClass() { value = 3.14159;}
};

   Класс AClass содержит один закрытый член value типа double. Конструктор класса присваивает этому члену значение 3.14159. За исключением этого действия, в классе не предусмотрены даже средства просмотра значения value объекта класса. Закрытый член находится в полной безопасности, как медвежонок под защитой мамаши-медведицы.
   Далее, предположим, что вы объявили еще один класс, содержащий членом объект класса AClass

class BClass {
private:
  AClass anObject; // член объекта класса AClass
public:
  void ShowValue(void){ cout << anObject.value; } // ???
};

   Член anObject типа AClass закрыт в классе BClass. Функция-член ShowValue() пытается отобразить value из anObject. Однако объявление не скомпилируется, поскольку член value закрыт в классе AClass и только функции-члены AClass имеют к нему доступ.
   Замена в классе AClass спецификатора доступа private на protected для члена value не решает задачу. Защищенный член доступен в классе, в котором он объявлен, и в любых производных из него классах. Несмотря на то что класс BClass обладает объектом типа AClass, два этих класса не связаны между собой отношением родства, и члены BClass не имеют доступа к закрытым и защищенным членам AClass. Конечно, вы можете сделать член value открытым в классе AClass, но это сделает value доступным и во всех операторах.
   Лучшее решение - сделать класс BClass другом класса AClass. Объекты BClass будут иметь доступ к value и всем другим закрытым и защищенным членам, а операторы вне двух классов будут лишены возможности доступа к запрещенным частям класса. Для этого следует указать ключевое слово friend внутри класса, к которому необходимо получить доступ в другом классе. В данном примере класс BClass получит доступ к закрытому члену value внутри AClass. Таким образом, для того, чтобы класс BClass получил доступ к этим закрытым данным в классе AClass, следует первый класс объявить дружественным второму. Вот новое объявление класса AClass:

class AClass {
  friend class BClass; // Класс BClass - друг класса AClass
private:
  double value; // Доступен членам классов AClass и BClass!!!
public:
  AClass() { value = 3.14159; }
};

   Единственное отличие от предыдущего объявления - строка friend class BClass, дающая указание компилятору предоставить классу BClass доступ к защищенным и закрытым членам класса AClass. Другие операторы в других классах и в основной программе, как и раньше, не имеют доступа к объявленным з защищенными членам класса AClass. Только BClass может частным образом проникнуть в закрытую "комнату" AClass. Таким образом, вы можете объявить любое число классов друзьями. Единственное ограничение - ключевое слово friend должно быть внутри объявления класса. Полезно запомнить еще некоторые факты.

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

   В листинге 15.1 демонстрируется, как дружественный класс получает доступ к закрытым и защищенным членам другого класса.

Листинг 15.1 FRIEND.CPP (использование дружественных классов)

1:  #include <iostream.h>
2:
3:  class Pal {
4:   friend class Buddy;         // Buddy - друг класса Pal
5:  private:
6:   int x;                      // Доступен членам Pal и Buddy
7:  protected:
8:   void doublex(void) {x *= x;}// Доступен в Pal и Buddy
9:  public:
10: Pal() {x = 100;}             // Доступен всем пользователям
11: Pal(int n) {x = n;}          // - " -
12:};
13:
14:class Buddy {
15: private:
16:  Pal palObject;
17: public:
18:  void ShowValues(void);
19: };
20:
21: main()
22: {
23:   Buddy aBuddy;
24:   
25:   aBuddy.ShowValues();
26: return 0;
27: }
28:
29: void Buddy::ShowValues(void)
30: {
31:   Pal aPal(1234);
32:
33:   cout << "\nBefore, palObject.x == " << palObject.x;
34:   palObject.doublex();
35:   cout << "\nAfter, palObject.x == " << palObject.x;
36:   cout << "\nPal.x == " << aPal.x << '\n';
37: }

   Модуль FRIEND начинается с объявления класса с именем Pal. В строке 4 указывается, что второй класс Buddy - друг класса Pal. Благодаря этой строке функции-члены класса Buddy имеют доступ к закрытым и защищенным членам объектов класса Pal. Конечно, класс Pal не имеет доступа к закрытым и защищенным членам Buddy.
   В строке 6 в классе Buddy объявляется объект класса Pal. Запустив на выполнение код, вы можете убедиться, что, хотя класс Buddy не состоит в родстве с Pal, функция-член ShowValues() имеет непосредственный доступ к закрытому члену х и к защищенной функции-члену doublex() (строки 29-37) класса Pal.

  следующая страница >>
главная - о проекте - контакты - реклама на сайте
 
LBN100 Elite

SoftStudio.Ru - студия разработки программ
LBN100 Elite