Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Бичков - Основи сучасного програмування.doc
Скачиваний:
69
Добавлен:
07.03.2016
Размер:
2.67 Mб
Скачать

Void Show(One &c1);

};

class One {

friend void Two::Show(One &c1);

private:

char*s1;

public:

One(){s1="testing";}

};

main()

{

One c1;

Two c2;

c2.Show(c1);

return 0;}

Void Two::Show(One &c1)

{cout<<c1.s1<<"\n";}

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

7.12. Перевантаження операцій

7.12.1. Загальний підхід

У С++ допускається перевантаження операцій. Така можливість надається при роботі з класами. Для перевантаження операцій використовується ключове слово operator. Щоб перевантажити операцію, необхiдно описати функцiю вигляду

<mun>operator<знак операцiї>(параметри).

Перевантажувати можна всi операцiї, крім "*", "::"," sizeof" та ".".

Існує два основних способи визначення перевантажених операцій:

1. Перевантажені операції оголошуються як функції, дружні до класу.

2. Перевантажені операції є членами класу.

Наприклад:

class complex{

public:

double re,im;

complex(){}

complex (double r,double i)

{re=r, im=i;}

friend complex operator+(complex,complex);

};

Void main()

{complex a=complex(1,3.1);

complex b=complex(1.2,2);

complex c=b;

a=b+c; //(1)

c=a+complex(1,2);} //(2)

complex operator+(complex a,complex b)

{complex c;

c.re=a.re+b.re;

c.im=a.im+b.im;

return c;}

Замiсть (1)–(2) можна записати:

а=operator+(b,c);

c=operator(a,complex(1,2));

Бачимо, що, оголосивши перевантажену операцію + як дружню функцію до класу, можемо вільно використовувати значок + відносно екземплярів класу. Причому в точках, де розташована перевантажена операція, відбувається виклик відповідної функції (у даному випадку – operatop+). Очевидно, що компілятор буде розрізняти + перевантажений і + звичайний. Якщо десь буде фігурувати звичайний арифметичний вираз, то жодних конфліктів не виникне. Іншими словами, перевантажуючи операції, ми ніби розширюємо поле їх дії на відповідні екземпляри класів. Порожній void-конструктор класу complex необхідний для коректного оголошення екземпляра класу c у функції operator+().

Перепишемо приклад так, щоб перевантажена операція була членом класу:

class complex {

public:

double re,im;

complex(){};

complex(double r,double i)

{re=r;im=i;}

complex operator+(complex);}

complex complex::operator+(complex a)

{

a.re+=re;

a.im+=im;

return a;}

Бачимо, що в перевантаженій операції +, на відміну від попереднього прикладу, є один формальний параметр типу complex. Другий просто не потрібен. Адже другим параметром виступає покажчик на об'єкт this, для якого викликається функція-член.

Для перевантажених операцій обов’язково має зберігатися така сама кількість операндів, яка була й у базовій операції. Наприклад:

class x {

friend x operator – (x);

friend x operator – (x,x)

friend x operator – (); //помилка

friend x operator – (x,x,x); //помилка};

У першій і другій перевантаженій операціях кількість операндів нормальна: перший мінус – унарний, другий – бінарний. У третій і четвертій є помилка, оскільки не існує базових операцій з відповідною кількістю операндів.

Згадаємо особливість мови С(С++), яка полягає у відсутності можливості роботи зі складеними типами даних як з одним цілим. Використовуючи перевантаження, ми можемо самі створювати потрібні типи. Розглянемо, наприклад, клас

class string {

private:

char value[100];

public:

string(){value[0]=0;}

string(const char*s)

{strcpy(value,s);}

long GetValue(void){return atol(value);}

friend long operator+(string a,string b);

friend long operator – (string a,string b);};

main()

{

string a="1234";

string b="4321";

cout<<"\na+b+6="<<(a+b+6);

cout<<"\na-b+10="<<(a-b+10)<<'\n';}

long operator+(string a,string b)

{return(atol(a.value)+atol(b.value));}

long operator – (string a,string b)

{return(atol(a.value) – atol(b.value));}

Перепишемо приклад, щоб перевантажені операції були членами класу:

class String {

private:

char value[100];

public:

String(){value [0]=0;}

String (const char*s);

long GetValue(void){return atol(value);}

long operator+(String b);

long operator – (String a);};

main()

{String a="1234";

String b="4321";

cout<<"\na+b+6="<<(a+b+6);

cout<<"\na-b+10="<<(a-b+10)<<'\n';

return 0;}

String::String(const char*s)

{strcpy(value,s);

}

long String::operator+(String b)

{return(atol(value)+atol(b.value));}

long String::operator – (String a)

{return(atol(a.value) – atol(value));}

Як видно з наведених прикладів, для будь-якої перевантаженої бінарної операції @ вираз aa @ bb інтерпретується як operator @ (aa,bb) чи aa. operator @ (bb) залежно від того, як визначена дана операція (функція-член чи функція-друг). Звідси бачимо, що перевантажена функція-член не може мати перший параметр-елемент основного типу. Адже 2 @ aa інтерпретується як 2.operator @ (aa), що не допускається.

Аналогічно для будь-якої унарної операції @ оператор aa@ чи @aa інтерпретується відповідно як operator @(aa) чи aa. operator @ ().