Բաժին 4.2
Օպերատորների ծանրաբեռնում (Overloading operators)

C++ը հնարավորություն է տալիս ծանրաբեռնել լեզվի ստանդարդ օպերատորները` սահմանելով տարբեր գործողություններ կլասերի համար: Օրինակ

int a, b, c;
a = b + c;

 

ճիշտ է, քանի որ int տիպերի համար գումարման գործողությունը արդեն սահմանած է: Սակայն չի կարելի գրել

 

struct { char product [50]; float price; } a, b, c;
a = b + c;

 

առանց սահմանելու գումարման գործողությունը: Միակ գործողությունը, որը սահմանված է` վերագրման գործողությունն է, որը թույլ է տալիս մի տիպի կլասի (կառուցվածքի) վերագրումը, մյուս` նույն տիպի կլասին (կառուցվածքին) (լռությամբ պատճենման կառուցիչ):

C++ում մենք կարող ենք սահմանել այն գործողությունները, որոնք դեռևս որոշված չեն տվյալ կլասի համար (օրինակ գումարման գործողությունը) և նույնիսկ կարող ենք ձևափոխել արդեն որոշված օպերատորները: Ստորև բերված է օպերատորների ցուցակ, որոնք կարող են ծանրաբեռնվել.

+    -    *    /    =    <    >    +=   -=   *=   /=   <<   >>
<<=  >>=  ==   !=   <=   >=   ++   --   %    &    ^    !    |
~    &=   ^=   |=   &&   ||   %=   []   ()   new  delete

 

Օպերատոր ծանրաբեռնելու համար անհրաժեշտ է գրել կլասի անդամ ֆունկցիա, որի անունը սկսվում է operator բառով և վերջանում օպերատորի նշանով, որը մենք ուզում ենք ծանրաբեռնել: Տեսքը.

 

տիպ operator նշան   (պարամետրեր);

 

Ստորև բերված է մի օրինակ, որում ծանրաբեռնվում է + օպերատորը: Այս ծրագիրը գումարելու է իրար երկու` a(3,1) և b(1,2), երկչափ վեկտորները: Երկու երկչափ վեկտորների գումարումը, նշանակում է նրանց համապատասխան կոորդինատների գումարումը, այսինքն այս դեպքում a+b վեկտորների գումարման արդյունքը պետք է լինի` (3+1,1+2) = (4,3) երկչափ վեկտորը.

 

// vektorner: operatorneri canrabernman orinak
#include <iostream.h>
 
class CVector {
  public:
    int x,y;
    CVector () {};
    CVector (int,int);
    CVector operator + (CVector);
};
 
CVector::CVector (int a, int b) {
  x = a;
  y = b;
}
 
CVector CVector::operator+ (CVector param) {
  CVector temp;
  temp.x = x + param.x;
  temp.y = y + param.y;
  return (temp);
}
 
int main () {
  CVector a (3,1);
  CVector b (1,2);
  CVector c;
  c = a + b;
  cout << c.x << "," << c.y;
  return 0;
}

4,3

 

CVector կլասի operator+ ֆունկցիան պատասխանատու է թվաբանական + օպերատորի ծանրաբեռնման համար: Այն կարող է կանչվել հետևյալ երկու ձևերով.
 

c = a + b;
c = a.operator+ (b);

 

Ուշադրություն դարձրեք նաև այն բանին, որ մենք հայտարարել ենք դատարկ կառուցիչ` դատարկ մարմնով.

 

CVector () { };

 

Սա պարտադիր է, քանի որ արդեն գոյություն ունի մեկ այլ կառուցիչ`

 

CVector (int, int);

 

և լռությամբ կառուցիչներից ոչ մեկը չի օգտագործվելու: Դրա համար մենք պետք է հայտարարենք այդպիսի կառուցիչը ինքներս, հակառակ դեպքում հետևյալ արտահայտությունը կհամարվի սխալ`

 

CVector c;

Սակայն դատարկ մարմնով կառուցիչներ ստեղծելը խորհուրդ չի տրվում, քանի որ դրանք չեն բավարարում կառուցիչների մինիմալ պահանջներին` փոփոխականների սկզբմարժեքավորմանը: Մեր դեպքում կառուցիչը թողնում է x և y փոփոխականներին անորոշ: Ճիշտ կլիներ այդ կառուցիչի փոխարեն գրել

CVector () { x=0; y=0; };

 

որը ծրագրի պարզության համար անտեսված է:

Լռությամբ որոշված վերագրման օպերատորը պատճենում է պարամետր օբյեկտի (աջ մասում գրված օբյեկտի) բոլոր ոչ ստատիկ ինֆորմացիոն անդամների պարունակությունը ձախ մասում գրված, նույն տիպի օբյեկտի մեջ: Բայց, իհարկե, դուք կարող եք սահմանել այն ձեր ուզած ձևով:

Չնայած նրան, որ պարտադիր չէ, որ ծանրաբեռնված գործողությունները համապատասխանեն մաթեմատիկական գործողություններին կամ դրանց իմաստին, սակայն խորհուրդ է տրվում պահպանել այս պայմանը: Օրինակ, ցանկալի չէ, որ + գործողությունով սահմանվի տարբերության գործողությունը:

Չնայած, որ operator+ ֆունկցիայի նախատիպը կարող է թվալ ակնհայտ, քանի որ այն վերցնում է օպերատորի աջ մասը, որպես operator+ ֆունկցիայի պարամետր, սակայն ուրիշ օպերատորների դեպքը այնքան էլ ակնհայտ չէ: Ստորև բերված է ցուցակ, թե ինչպես պետք է հայտարարվեն տարբեր օպերատոր ֆունկցիաները (փոխարինեք @ սիմվոլը օպերատորով).

Արտահայտություն

Օպերատոր (@)

Անդամ ֆունկցիա

Գլոբալ ֆունկցիա

@a

+ - * & ! ~ ++ --

A::operator@()

operator@(A)

a@

++ --

A::operator@(int)

operator@(A, int)

a@b

+ - * / % ^ & | < > == != <= >= << >> && || ,

A::operator@(B)

operator@(A, B)

a@b

= += -= *= /= %= ^= &= |= <<= >>= [ ]

A::operator@(B)

-

a(b, c...)

()

A::operator()(B, C...)

-

a->b

->

A::operator->()

-

 

որտեղ a –ն` A կլասի օբյեկտ է, b –ն` B կլասի օբյեկտ է,  c –ն` C կլասի օբյեկտ է:

Այս ցուցակից դուք տեսնում եք, որ կա օպերատորներ ծանրաբեռնելու երկու եղանակ` որպես անդամ ֆունկցիա կամ որպես գլոբալ ֆունկցիա: Երկրորդ` գլոբալ ֆունկցիյա  դեպքը մենք կքննարկենք ավելի ուշ:

 

this Բանալի-բառը (The keyword this)

 

Բանալի-բառ this–ը կլասի սահմաններում ցույց է տալիս տվյալ օգտագործվող կլասի օբյեկտի` հիշողության մեջ զբաղեցրած հասցեն:

 

Այն կարող է օգտագործվել, օրինակ, ստուգելու, թե արդյոք փոխանցված պարամետրը հենց ինքը` օբյեկտն է, թե ոչ: Օրինակ.

// this
#include <iostream.h>
 
class CDummy {
  public:
    int isitme (CDummy& param);
};
 
int CDummy::isitme (CDummy& param)
{
  if (&param == this) return 1;
  else return 0;
}
 
int main () {
  CDummy a;
  CDummy* b = &a;
  if ( b->isitme(a) )
    cout << "ayo, &a-n b-n e!";
  return 0;
}

ayo, &a-n b-n e!

this–ը նաև սովորարաբար օգտագործվում է operator= անդամ ֆունկցիաներում, որոնք վերադարձնում են օբյեկտներ՝ հղումով (&): Նախորդ վեկտորների օրինակում մենք կարող էինք գրել operator= ֆունկցիան հետևյալ կերպ.

CVector& CVector::operator= (const CVector& param)
{
  x=param.x;
  y=param.y;
  return *this;
}
 
Փաստարոն սա լռությամբ կոդն է, որն ավելացվում է մեր կլասին, երբ մենք չենք սահմանում operator=  անդամ ֆունկցիան:
 

Ստատիկ անդամներ  (Static members)

 

Կլասը կարող է պարունակել ստատիկ անդամներ, փոփոխականներ կամ ֆունկցիաներ:

 

Կլասի ստատիկ անդամները նաև հայտնի են «կլասի փոփոխականներ» անունով, քանի որ նրանց պարունակությունը կախված չէ օբյեկտներից, այլ ֆիքսած է (միևնույնն է) այդ կլասի բոլոր օբյեկտների համար:

Օրինակ գրենք մի ծրագիր, որում կլասի ստատիկ անդամը ցույց տա, թե այդ կլասի տիպի ինչքան օբյեկտ է հայտարարված.

// klasi statik andamner
#include <iostream.h>
 
class CDummy {
  public:
    static int n;
    CDummy () { n++; };
    ~CDummy () { n--; };
};
 
int CDummy::n=0;
 
int main () {
  CDummy a;
  CDummy b[5];
  CDummy * c = new CDummy;
  cout << a.n << endl;
  delete c;
  cout << CDummy::n << endl;
  return 0;
}

7
6

Փաստորեն, ստատիկ անդամները ունեն նույն հատկությունները, ինչ գլոբալ փոփոխականները, բայց գտնվում են կլասի տեսանելիության տիրույթում: Ընդունված է նաև տալ ստատիկ անդամի նախատիպը (նկարագրությունը) կլասի մեջ, իսկ որոշումը (սկզբնարժեքավորումը)` կլասից դուրս` գլոբալ տեսանելիության տիրույթում, ինչպես և արված է նախորդ ծրագրում:

Քանի որ ստատիկ անդամը միևնույն փոփոխականն է իր բոլոր կլասի օբյեկտների համար, այն կարող է դիմվել, որպես ցանկացած տվյալ կլասի օբյեկտի անդամ կամ նույնիսկ կլասի անունով (սա ճիշտ է միայն ստատիկ անդամների համար):

cout << a.n;
cout << CDummy::n;

 

Այս երկու կանչերն էլ ցույց են տալիս միևնույն փոփոխականին` CDummy կլասի n ստատիկ փոփոխականին:

Ինչպես մենք նոր կլասի մեջ օգտագործեցինք ստատիկ փոփոխական, այնպես էլ մենք կարող ենք օգտագործել ստատիկ ֆունկցիաներ: Սրանց իմաստը նույնն է` սրանք գլոբալ ֆունկցիաներ են, որոնք կանչվում են, իբրև դրանք տվյալ կլասի օբյեկտի անդամ ֆունկցիաներ են: Սակայն ստատիկ ֆունկցիաները կարող են օգտագործել միայն ստատիկ անդամներ, այսինքն, նրանք չեն կարող դիմել տվյալ կլասի ոչ ստատիկ անդամներին, քանի որ դրանք արդեն կախված են օբյեկտից, ինչպես նաև չեն կարող օգտագործել this բանալի-բառը:

Նախորդը | Հաջորդը