Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /home/studb20/public_html/index.php on line 4
 2.6.3 Друзі-класи та друзі-функції - Програмування С, С++теорія та практика (частина 2) - Studbook
Главная->Інформатика та програмування->Содержание->2.6.3 Друзі-класи та друзі-функції

Програмування С, С++теорія та практика (частина 2)

2.6.3 Друзі-класи та друзі-функції

Роль специфікаторів доступу, як відмічалося раніше, полягає в обмеженні доступу, тобто захисті інформації, що є одним з елементів об’єктного підходу. Звичайно, концепція приховування даних існує не для того, щоб її порушували, проте може статися такий випадок, коли необхідно, як виняток, забезпечити доступ конкретній функції або класу до елементів протокольної частини, специфікованими як ргіуаіе або ргоіеєіесі. Семантика мови дозволяє оголосити два види таких “друзів”- один клас як єдине ціле може бути другом іншого класу, або другом може бути оголошена окрема зовнішня функція.

У тому випадку, коли не йде мова про успадкування між класами, є можливість дозволити доступ до будь-яких елементів класу іншому класу або функції за допомогою зарезервованого слова /гіепС. Так, дійсно, це порушення обмеження доступу, проте іноді воно буває корисним, якщо класи не можуть бути пов’язані відношенням успадкування. Наприклад, наступний фрагмент не скомпілюється, видавши повідомлення про помилку:

сіазз К.оот { рг^аЬе:

СоиЬіе здиаге; риЬііс:

К.оот()

{

здиаге=20.5;

}

};

сіазз Ноизе { рг^аЬе :

К.оот оЬ^есЬ; риЬііс:

Vоіс^ зЬом ^оіС.)

{

// саппої ассе88 ргіуаіе шешЬег гїесіагегї іп сіакк 'Коот'І!! соиЬ<< оЬ^есЬ. здиаге; //не має доступу до ргіуаіе-елемента!

};

Елемент і'диаге прихований в класі Коот, і навіть заміна специфікатору на ргоіеєіесі не вирішить проблеми, оскільки класи не пов’язані відношенням успадкування. Тільки віднесення хдиаге до риЬііс-частини надасть бажаного результату, проте і це не вихід: у такому випадку й інші класи матимуть необмежений доступ до елемента-даних. Існує альтернатива, якщо оголосити клас Ноизе дружнім до класу Коот:

с1азз К.оот_ {

£гіепй с1азз Ноизе;

// далі повний протокол класу Коот;

}

Такий опис надасть компілятору вказівку забезпечити класу Ноизе повний доступ до захищеної та закритої частин класу Коот (кажуть, клас Ноизе є дружнім класом по відношенню до класу Коот). Існує ряд правил відносно класів-друзів:

•        Якщо клас  ОМЕ оголошує клас Т№О дружнім для себе, то це не

означає, що                клас ОМЕ буде також дружнім до класу Т№0 (не існує

взаємності).

•        Якщо клас ОМЕ оголошує клас Т№0 дружнім для себе, класи, похідні від класу Т№0, не будуть автоматично отримувати доступ до елементів класу ОМЕ, тобто не будуть його друзями (друзі не успадковуються).

•        Якщо клас  ОМЕ оголошує клас Т№О дружнім для себе, класи,

похідні від  класу ОМЕ, не будуть автоматично отримувати нові

властивості, як друзі класу Т№О.

•        Специфікатори доступу не впливають на описи /гіепсі.

Найчастіше оголошення класів друзями є примусовим та свідчить про не досить вдалу продуманість типової ієрархії. Дійсно, якщо є необхідність у такій "співдружності", чи не було доцільним передбачати такий зв'язок на початковій стадії проектування, застосувавши механізми успадкування? У будь-якому разі, користуватися друзями в Сі++ слід лише у випадках крайньої необхідності.

Подібно дружнім класам, можливим є оголошення функцій-друзів

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

•        дружня функція оголошується в середині класу, до елементів якого потрібен доступ, з ключовим словом /гіепії. Найчастіше у якості параметра такій функції повинен передаватися сам об’єкт, або

посилання на об’єкт класу, оскільки покажчик ікі$ їй не передається.

•         дружня функція може бути звичайною функцією або методом іншого визначеного класу. На цю функцію не поширюються дії специфікаторів доступу, не має значення конкретне місце її розташування у протоколі.

•         одна функція може бути дружньою по відношенню до декількох класів.

Для початку розглянемо випадок, коли дружня функція Ехіегпаі() до класу А є зовнішньою:

с1азз Опе { рг^а^е: сЬаг*з^г; риЬ1іс:

Опе(){ з^г="123";}

£гіепС. VОіС. Ітрог'Ьап'Ье (Опе &а);

//оголошення Ггіепгї - функції

};

VОіС. Ех'Ьегпа1 (Опе &а) ; іп£ таіп^оіС.)

{

Опе г ;

Ех'Ьегпа1(г); ге'Ьигп 0;

}

VОіС. Ех'Ьегпа1 (Опе &а) {

сои^<< а. з^г<<епЛ1;  // доступ до елемента-даних

// сЬаг*8Іг з секції ргіуаіе

}

Таким чином, оголошення Угіепгі-фушції 1трогіапіе() у класі Опе відкриває їй доступ до закритих та захищених членів даного класу. Одна й та сама функція може бути оголошеною як /гіепй двом класам одночасно. Іноді це зручно, скажімо у випадках одночасної ініціалізації даних, хоча такі варіанти організації спричиняють неявний зв'язок між класами, їх подальшу залежність, що не є бажаним. Інша справа, коли класи пов'язані логікою програми:

сіазз ЗЬиСепЬ; // неповне оголошення класу сіазз ТеасЬег{

£гіепС VоіС гедізЬгаЬіоп() ; ргоЬесЬеС: іпЬ НзЬиСепЬз;

ЗЬиСепЬ *ріізЬ[100];

/// і т. д.

};

сіазз 8ЬиСепЬ{

£гіепС VоіС гедізЬгаЬіоп(); ргоЬесЬеС:

ТеасЬег *ТсЬ; іпЬ зетезЬегЬоигз;

/// і т. д.

};

У вищенаведеному прикладі функція гедііігаііоп() може застосовуватися як класом Теаєкег, так і класом 8іиСепі, не знаходячись у жодному з них, але пов'язуючи їх під час реєстрації (ініціалізації), що цілком нормально, виходячи з даного контексту.

Тепер щодо випадку, коли використовуються функції-друзі як елементи-функції. Зазвичай, дружньою у класі оголошується елемент- функція іншого класу. Ця функція матиме вільний доступ до захищеної та закритої частин класу, в якому вона оголошена як /гіепС. На практиці це може виглядати таким чином:

сіазз А; // неповне оголошення класу сіазз В {

рг^аЬе: сЬаг *з2; риЬііс:

В() { з2= "В! !! "; }

VоіС зЬом(А &с1);

};

сіазз А {

£гіепС VоіС В::зЬом(А&с1); рг^аЬе: сЬаг *з1; риЬііс:

А() {з1="А !!!";}

};

VОій. таіп ()

{

А сі ;

В с2 ;

с2 . зЬом (сі) ;

}

VОій. В: :зЬом(А&сі)

{

сои'Ь<< сі. зі<<з2<<епй1;

}

В реалізації видно, як функція зком>() звертається до закритої частини обох класів, хоча належить лише одному з них - класу В. Слід відмітити, що клас, в якому міститься прототип елемента-функції, повинен оголошуватися раніше класу, що вказує на функцію-елемент як дружню. Так для класу А, який оголошує дружньою для себе функцію В::зком?(А&с1), оголошення класу В попередньо повинно бути доступним компілятору.

Дружність у будь-якому випадку є порушенням цілісності даних, тому навряд чи є сенс у тому, аби надмірно зловживати нею. Деяке конкретне використання /гіепсі-функщї знаходять саме при перевантаженні операторів при маніпуляції екземплярами класів, про що йтиметься в наступних розділах.

 

32