{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 @ ().
Перетворення типів
Програміст має сам подбати про перетворення типів при роботі з різнотипними даними, де використовуються його власні типи даних. Операції перетворення типів за умовчанням при роботі з типами користувача зазвичай не працюють. Виняток становить так зване неявне перетворення типів, про яке буде сказано далі. Для забезпечення роботи з різнотипними даними існує кілька підходів:
1. Використання перевантажених функцій з різними типами параметрів:
friend complex operator+(complex,complex);
friend complex operator+(complex,double);
………………..
complex b(2,2),c(3,3);
b=c+20+b;
2. Використання конструкторів, які виконують відповідні перетворення типів. Мається на увазі виклик відповідних конструкторів у ситуаціях, коли замість значення деякого типу за синтаксисом має бути об'єкт.
Розглянемо описаний вище клас string.
У рядках
string a="1234";
string b="4321";
відбувається, фактично, виклик відповідних конструкторів, а потім присвоєння екземплярам а та в відповідних абстрактних об'єктів. Аналогічні виклики відбуваються й у випадках, коли різнотипні дані зустрічаються в деяких арифметичних виразах. Наприклад:
class complex {
//...
complex(double f){re=f;im=0;}
};
Можна було б записати конструктор і так:
class complex {
//…
complex(double f,double i=0){re=f,im=i;}
};
Тоді в main()- функції можемо записати:
complex a(1,2),b(3);
a=b+121;
В останньому рядку відбувається неявний виклик конструктора complex(121), а потім виконується перевантажена операція додавання для екземплярів класу complex. Якщо конструктор наведеного вище типу не визначений, то можемо в даній ситуації зробити так:
a=operator+(b,complex(double(121),double(0)));
Використання конструкторів має деякі недоліки:
1. Виникають труднощі з неявним перетворенням типу від типу користувача до основного.
2. Не може бути неявного перетворення від нового типу до старого без зміни старого.
Позначимо через Т ім'я типу, X – деякий клас, тоді функція-член
X::operator Т();
здійснює перетворення типу з X на Т. Якщо ми використаємо описаний вище клас string та ініціалізуємо екземпляр класу
string myvalye="1234";,
то рядок long x=myvalue; буде помилковим, оскільки в класі не передбачено перетворення рядка на довге ціле.
Вихід з такої ситуації – перевантаження операції перетворення типу. У класі string необхідно описати функцію вигляду
operator long(){return atol(value);}
При роботі з різнотипними даними слід уникати можливих неоднозначностей. Розглянемо приклад:
class x{
public:
x(int){};
x(){};
};
class y {
public:
y(int){};
};
class z {
public:
z(x){};
z(y){};
};
Main()
{
z f(1);
}
При оголошенні екземпляра класу z f(1) у функції main() виникає неоднозначність. Згідно з оголошенням параметром конструктора класу z має виступати екземпляр або класу x, або y. Оскільки тут стоїть одиниця й у класах є конструктори, що дозволяють здійснити перетворення від цілого до типу відповідного класу, то таке перетворення має бути здійснене. Однак у якому класі це відбудеться, якщо вони обидва містять відповідні конструктори? Компілятор відстежує такі неоднозначності.
Для оптимізації роботи програми як формальні параметри перевантажених операцій краще використовувати посилання:
class matrix {
double m [300][400];
public:
matrix (){};
friend matrix operator+(matrix &,matrix &);};
Це дозволить уникнути копіювання даних, що займають значний обсяг пам'яті, у стек.