Hibakeresés túlterhelt operátorokban

Döntsük el, okoznak e hibát az alábbi programrészletek! Ha igen, adjuk meg, hogy fordítási vagy futási idejű-e a hiba, illetve a hiba helyét és magyarázatát! Javasoljunk megoldást, ha van hiba! A programrészletekben elírások nincsenek, az összes meghívott függvény ill. hivatkozott osztály létezik, ha nincs megadva a kódrészletben, akkor feltételezzük a helyes működést.

Ez a feladat főként a zárthelyire való felkészülést segíti, mindenképp érdemes a számonkérés előtt áttanulmányozni.

a)
String::String(
const String& theOther)

{

      ...

            *this=theOther;

}

 

String & String::operator=(String theOther)

{

      ...

};


b)
Vector:: Vector (const Vector & theOther)

      ...
      *this=theOther;
}

Vector Vector::operator=(Vector& theOther)

{

      ...

};

 

c) 

String& String::operator+ (String theOther)

{

      ...

      return theOther;

}

 

d)

String& String::operator+ (String theOther)

{

      String s;

      ...

      return s;

}

 

e) 

Vector::Vector (const Vector theOther)

{

      ...

      *this=theOther;

}

 

Vector & Vector::operator=(Vector theOther)

{

      ...

};

 

 

f)

String& String::operator=(String theOther)

{

      ...

      return theOther;

}

 

g)

 

Vector & Vector::operator+ (Vector theOther)

{

      Vector s;

      ...

      return s;

}

 

h)

void String::operator=( String theOther)

{

      ...

      return *this;

};

...

String s1,s2,s3("Santa Claus");

s1=s2=s3;

 

 

i)

Complex & Complex::operator− (Complex theOther)

{

      return theOther;

}

 

 

 



A megoldásért kattints ide!

A megoldáshoz fontos, hogy tudatosítsuk: a túlterhelt operátorok tulajdonképpen függvények (leszámítva a speciális kiértékelést híváskor és a speciális szintaxist). Ez azt jelenti, hogy lokális változó/érték szerint átvett argumentum címét továbbra sem szolgáltathatjuk ki a függvényen kívülre sem pointeren sem referencián keresztül. Ez a hiba futási időben jön elő, bár warningot kapunk. (SzC++Ny 2.4 fejezet).

A másik fontos dolog az = operátor és a másolókonstruktor közötti összefüggés: ha a másolókonstruktorból az =operátort hívjuk, akkor az = operátorban nem használhatunk érték szerinti átadást, hiszen az a másolókonstruktor további hívását jelentené, amely végtelen ciklust eredményezne (SzC++Ny 6.4 fejezet eleje). Mivel a fordító ezt nem ellenőrzi (warningot itt is ad), ez a hiba is csak futási időben derül ki a verem túlcsordulásával, ami a végtelen rekurzió következménye.

Megjegyezzük még, hogy a hiba többféleképpen kijavítható, bármely megoldás ugyanolyan mértékben elfogadott, az „elegánsabb” megoldások a szokásos megoldásra próbálnak rámutatni.

Ezek után vegyük sorra a feladatokat!

 

a)  
Itt az =operátort hívjuk a másolókonstruktorból, vagyis érték szerinti paraméterátadás az operátor=-ben végtelen ciklust okoz. A paraméterlistában pontosan ez történik, amennyiben a paramétert referencia szerint vesszük át, akkor kijavítjuk a hibát.

 

b)
Itt a hiba nagyon hasonló az előzőhöz, csak itt a visszatérési értéket kell referencia szerint visszaadnunk.

 

c)
Itt egy érték szerint átadott argumentumot adunk vissza referencia szerint. Vagy érték szerint kell visszaadnunk, vagy referencia szerint kell átvennünk. Különösen elegáns, ha konstans referenciaként vesszük át, ez a megoldás a + operátor szokásos használatát is figyelembe veszi. (A mellékhatás a + operátor argumentumain ritkán tekinthető követendő megoldásnak.)

 

d)
A kódrészlet egy lokális változót ad vissza referencia szerint. A hiba javításaként érték szerint kell visszaadnunk a lokális változót, és nem referencia szerint.

 

e)
A kódrészlet az a) és b) feladat hibáit egyesíti. A hiba javítása is azonos: referencia szerint vesszük át az argumentumot, és referencia szerint térünk vissza.

 

f)
A kódrészletben található hiba megegyezik a c) feladatnál tapasztalttal. Az ott bemutatott megoldások helyesek, ugyanakkor mivel az = operátorról van szó, konstans referenciaként vesszük át a paramétert, ez a szokásos megoldás.

 

g)
Itt a d) p
éldával analóg a hiba, javítása ugyancsak érték szerinti visszatéréssel.

 

h)
Ebben a feladatban nincs benne a másolókonstruktor, vagyis elképzelhető, hogy nem hívja az = operátort. Nem szép megoldás, ne kövessük, de nem is hiba. Viszont az s1=s2=s3 kifejezés átírva:

operator = (s1, operator= (s2, s3))

Ebből az alakból jól látható, hogy az =operátornak szükség van a visszatérési értékére, amely pedig void. A függvényhívás paramétereinek biztosítása a fordító (compiler) feladatkörébe tartozik, ezért ez fordítási hiba. Egy másik fordítási hiba, hogy void visszatérésű függvény nem térhet vissza értékkel. A javítást valamelyest a példa is sugallja: a visszatérési értéket javítsuk String&-ra, és különösen elegáns konstans referenciával visszatérni, bár a példában az érték szerinti paraméterátvétel meglehetősen nehézzé teszi az elegáns végeredményt csak a hibákat javítva.

 

i)
A kódrészlet ismét variáció egy témára (c), f)): érték szerint átvett argumentummal térünk vissza referencia szerint. A problémát a szokásos módszerrel orvosolhatjuk, a legelegánsabb a konstans referencia szerinti paraméterátadás.




2008.01.19. 19:09:41 |  Permalink  |  Hozzászólások száma: 2  |  Tárgyszavak: Operátor túlterhelés


Írja meg Ön is véleményét!


Hozzászólásokat csak regisztrált, bejelentkezett felhasználóktól tudunk elfogadni!

Hozzászólások


Dániel Dániel  (2016.02.16. 2:30:45)

h) Nem elegáns értékadás operátorral konstans referencia szerint visszatérni, mert sérül a típustámogatás, azaz az operátor nem fog úgy működni, ahogyan azt a felhasználó a nem túlterhelt változatainak működését alapul véve elvárhatná. Érdekes dolog, hogy a beépített típusok közötti érvényes értékadásnak érték adható. Ha nem akarunk nagy meglepetést okozni az osztályunk gyakorlott c++ programozó felhasználóinak, akkor ez a tulajdonsága a mi értékadás operátorunknak is meg kell, hogy legyen.

Dániel Dániel  (2016.02.16. 2:14:54)

b) A fordítási hibát nem az érték szerinti visszatérés okozza, hanem a konstanshelyesség megsértése, pontosabban az, hogy a Vector Vector::operator=(Vector&) operátor konstansként értelmezett objektumot akar át venni nem konstans referencia szerint. A fordítási hibát két kézenfekvő módon lehet elkerülni: a) az értékadás operátor konstans referencia szerint veszi át az argumentumot, azaz a paramétere konstans referenciaként van deklarálva b) a másoló konstruktor nem konstans referencia szerint veszi át az argumentumot, azaz a paramétere nem konstans referenciaként van deklarálva Az előbbi a jobb megoldás, mert ebben az esetben a konstans lvalue objektumok, és adott esetben az rvalue objektumok is átadhatók a másoló konstruktornak és a másoló értékadás operátornak. (mozgató konstruktor és mozgató értékadás operátor hiánya az adott eset) Mindazonáltal a hatékonyság érdekében a visszatérési értéket érdemes lvalue referenciának deklarálni. Azért nem rvalue referenciának, mert akkor a felhasználó tudta nélkül kellene a visszatérített objektumot rvalue-vá és így jó eséllyel mozgathatóvá tenni egy kasztolással, holott a visszatérített objektum a későbbiekben még jó eséllyel elérhető a felhasználó számára. Azért nemkonstans lvalue referencia szerint, mert a típustámogatást adnánk fel konstans lvalue referenciával.