c++ operator overloading

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ha Allemaal,

Ik probeer mezelf een beetje c++ bij te brengen door opgaven te maken van een of andere tutorial. ik heb al wel wat programmeer ervaring met java maar loop nu toch tegen iets aan waar ik niet uit kom.

(de teller en noemer zijn private waarde, er mogen geen methoden returnTeller of returnNoemer bestaan)

Wat ik probeer te maken is van een klasse Rationaal (teller/noemer) een operator te maken die twee rationaal objecten kan optellen bij elkaar. Dus een operator+ voor twee rationaal objecten.

Voor het overzicht is er een Rationaal object A (3/6) gedefinieerd en een Rationaal object B (9/12) gedefinieerd. Result zal, na het toepassen van euclides (reduceren) moeten worden 2/3.

De operator heb ik nu als volgt:

code:
1
2
3
4
5
6
7
8
9
10
11
12
///////////////////////////////////////////////////////////////////////
// PROCEDURE: operator+        
// POST: Rationele getallen bij elkaar opgeteld
//
Rationaal Rationaal::operator+ (const Rationaal & rhs) const
{ 
  cout << "\nCalling operator+()" << endl;
  Rationaal result (*this);
  
  return result;
  //reduceer();
}


Waar ik niet achter kom is hoe ik de teller en noemer nu uit de objecten haal. Kan iemand me helpen?

Acties:
  • 0 Henk 'm!

  • Comp_Lex
  • Registratie: Juni 2005
  • Laatst online: 23:50
De variabelen zijn private, dus daar kom je nooit zomaar direct bij. Je moet ze bijvoorbeeld als public declareren of je moet toch gebruik maken van getters.

Sorry. Jullie hebben gelijk.

[ Voor 10% gewijzigd door Comp_Lex op 07-06-2010 14:28 ]


Acties:
  • 0 Henk 'm!

  • The Flying Dutchman
  • Registratie: Mei 2000
  • Laatst online: 29-07 21:57
Comp_Lex schreef op maandag 07 juni 2010 @ 12:40:
De variabelen zijn private, dus daar kom je nooit zomaar direct bij. Je moet ze bijvoorbeeld als public declareren of je moet toch gebruik maken van getters.
Aangezien je een Rationaal klasse hebt en de operator+ een member functie van de klasse Rationaal is (kan ook buiten de klasse gedefinieerd worden mocht je dat perse willen, dan heb je een friend declaratie nodig), heb je gewoon direct toegang tot de private members.

Je kunt dus binnen die functie gewoon rhs.teller en rhs.noemer gebruiken.

[ Voor 5% gewijzigd door The Flying Dutchman op 07-06-2010 12:45 ]

The Flying Dutchman


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Comp_Lex moet even goed lezen, de operator+ is als member gedefinieerd.

Je kan met
C++:
1
2
rhs.teller = 10; // ander object
teller = 11; // eigen object

de members bewerken.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
HighGuy,

Thanks, Ik wist niet dat ook kon in c++, thanks.

Ik zit nu alleen met een vervolgvraag.

code:
1
2
3
4
5
Rationaal Rationaal::operator+ (const Rationaal & rhs) const
{ 
  cout << "\nCalling operator+()" << endl;
  return Rationaal(t + rhs.t, n + rhs.n);
}


Aanroep als volgt:

code:
1
2
3
4
Rationaal * pRat = new Rationaal(3,6);
Rationaal * fRat = new Rationaal(9,12);
pRat = pRat + fRat;
pRat->print(); // print het antwoord uit


geeft terug: invalid operands of types 'Rationaal' and 'Rationaal' to binary 'operator+'

Wat ik niet begrijp omdat de operands 2 Rationalen zouden moeten zijn. Wat zie ik over het hoofd

Acties:
  • 0 Henk 'm!

  • Comp_Lex
  • Registratie: Juni 2005
  • Laatst online: 23:50
Je probeert hier pointers bij elkaar op te tellen. Je moet ze volgens mij dereferencen met '*'.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

De new operator is om objecten op de heap te maken, en daar krijg je dan ook pointers naar terug.
Daarna probeer je pointers op te tellen, wat niet de bedoeling is :)

Als je je instances echt op de heap wilt hebben (wat me sterk lijkt in dit geval), moet je schrijven:
C++:
1
*pRat = *pRat + *fRat

Tevens zie ik geen delete calls voor jouw heap objecten, dus heb je nu een memory-leak (of je hebt niet al je code geplakt)

Je kan ook gewoon stack variabelen gebruiken, dan heb je geen new en geen delete nodig:
C++:
1
2
3
4
Rationaal pRat(3,6); //ook mogelijk: Rationaal pRat = Rationaal(3,6);
Rationaal fRat(9,12);
pRat = pRat + fRat;
pRat.print(); // print het antwoord uit

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

Overigens klopt er natuurlijk geen reet van je optelling :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 00:53

Reptile209

- gers -

.oisyn schreef op maandag 07 juni 2010 @ 16:56:
Overigens klopt er natuurlijk geen reet van je optelling :)
Hehehe, da's nog wel een aardige, TS zit even fundamenteel verkeerd te denken.
Verwijderd schreef op maandag 07 juni 2010 @ 12:29:
[..]Voor het overzicht is er een Rationaal object A (3/6) gedefinieerd en een Rationaal object B (9/12) gedefinieerd. Result zal, na het toepassen van euclides (reduceren) moeten worden 2/3.
[..]
Verwijderd schreef op maandag 07 juni 2010 @ 14:30:
code:
1
2
3
4
5
Rationaal Rationaal::operator+ (const Rationaal & rhs) const
{ 
  cout << "\nCalling operator+()" << endl;
  return Rationaal(t + rhs.t, n + rhs.n);
}
Volgens mij is 3/6 + 9/12 = 6/12 + 9/12 = 15/12 = 5/4 = 1.25 (en dus niet: 3/6 + 9/12 = 12/18 = 2/3 = 0.666...). Check dus niet alleen je code, maar ook je logica ;).
Wat je nu doet, geldt wel voor vermenigvuldigen (teller * teller; noemer * noemer).

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

Idd. a/b + c/d = ad/bd + cb/bd = (ad+cb)/bd. En dat kun je vervolgens weer vereenvoudigen (waarschijnlijk met de reduceer() functie die je al had)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
De nette oplossing is

C++:
1
2
3
4
inline Rationaal operator+(Rationaal lhs, Rationaal const& rhs) {
  lhs += rhs;
  return lhs;
}

Lijstje verschillen:
1. operator+ is geimplementeerd via operator+=. Dit voorkomt dat je het algortime dubbel moet implementeren.
2. operator+ is een vrije functie. Dit zorg ervoor dat impliciete conversies symmetrisch zijn. Als Rationaal(1,3)+2 werkt, dan moet 2+Rationaal(1,3) ook werken.
3. De functie is inline want triviaal
4. Ik vermijd het maken van tijdelijke objecten door een (non-reference) input te retourneren.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

MSalters schreef op dinsdag 08 juni 2010 @ 18:42:
De nette oplossing is
...
4. Ik vermijd het maken van tijdelijke objecten door een (non-reference) input te retourneren.
is je lhs nu niet een temporary, die moet wel op de stack gezet worden tijdens de aanroep, en daarna gekopieerd in je resultaat

[ Voor 37% gewijzigd door MLM op 08-06-2010 19:30 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
MLM schreef op dinsdag 08 juni 2010 @ 19:30:
[...]

is je lhs nu niet een temporary, die moet wel op de stack gezet worden tijdens de aanroep, en daarna gekopieerd in je resultaat
Dat is toch geen probleem?

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Nee, maar dan is het net een temporary, en ik las zijn punt 4 als een claim dat die er niet waren :) Er zijn afaik geen manieren om een operator + te schrijven zonder een "extra" instantie van je object ergens, dan moet je toch naar een named functie (met een non-const ref argument om naar te schrijven), maar dat is niet echt een probleem met een object zo klein als deze :)

-niks-


Acties:
  • 0 Henk 'm!

  • The Flying Dutchman
  • Registratie: Mei 2000
  • Laatst online: 29-07 21:57
MSalters schreef op dinsdag 08 juni 2010 @ 18:42:
De nette oplossing is

C++:
1
2
3
4
inline Rationaal operator+(Rationaal lhs, Rationaal const& rhs) {
  lhs += rhs;
  return lhs;
}

Lijstje verschillen:
1. operator+ is geimplementeerd via operator+=. Dit voorkomt dat je het algortime dubbel moet implementeren.
2. operator+ is een vrije functie. Dit zorg ervoor dat impliciete conversies symmetrisch zijn. Als Rationaal(1,3)+2 werkt, dan moet 2+Rationaal(1,3) ook werken.
3. De functie is inline want triviaal
4. Ik vermijd het maken van tijdelijke objecten door een (non-reference) input te retourneren.
Goed punt, binary operators zouden inderdaad als vrije functies gedefinieerd moeten worden. Anders zijn promotions van left hand side argumenten niet mogelijk (en zou 2 + Rationaal niet mogelijk zijn, want 2 is geen Rationaal en kan alleen tot Rationaal gepromoveerd kunnen worden in een functie argument).

Wat punt 4 betreft, operator+ moet een nieuw object retourneren (operator+ wijzigt niet één van de operands, er moet dus een nieuw object, een resultaat, komen) en dus moet er sowieso een nieuw object op de stack geplaatst worden. In dit geval gebeurt dat door een kopie te maken van het argument van de functie (inderdaad door een non-reference object mee te geven). Echter, moderne compilers optimaliseren dit gewoon met behulp van copy elision:
Wikipedia: Return value optimization
Wikipedia: Copy elision
http://www.icce.rug.nl/do...lus/cplusplus08.html#l142

The Flying Dutchman


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

Daarbij wordt ook maar even vergeten dat je de += niet kunt implementeren zonder temporaries, in het geval van een Rationaal.

[ Voor 14% gewijzigd door .oisyn op 09-06-2010 12:02 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • The Flying Dutchman
  • Registratie: Mei 2000
  • Laatst online: 29-07 21:57
.oisyn schreef op woensdag 09 juni 2010 @ 12:01:
Daarbij wordt ook maar even vergeten dat je de += niet kunt implementeren zonder temporaries, in het geval van een Rationaal.
Wat bedoel je hier precies mee?

Onderstaande code genereert volgens mij geen temporaries:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
inline Rationaal &Rationaal::operator+=(Rationaal const &other)
{
  // zorg ervoor dat noemers van beide Rationaal objecten gelijk zijn
  d_teller *= other.d_noemer;
  d_noemer *= other.d_noemer;

  // de teller van other vermenigvuldigen met noemer van this om dezelfde noemers te verkrijgen, dan optellen
  d_teller += other.d_teller * d_noemer;
  
  // reduceer het huidige object indien mogelijk
  reduceer();

  return *this;
}

The Flying Dutchman


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

other.d_teller * d_noemer is een temporary. Maar goed, uiteindelijk hebben we het natuurlijk nergens over. Het zijn twee fucking floats, en menig compiler zal de boel gewoon in registers houden :)

[ Voor 69% gewijzigd door .oisyn op 09-06-2010 12:32 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
MLM schreef op dinsdag 08 juni 2010 @ 19:30:
[...]

is je lhs nu niet een temporary, die moet wel op de stack gezet worden tijdens de aanroep, en daarna gekopieerd in je resultaat
Nee, niet met de huidige generatie compilers. Die kijken waar de output uiteindelijk terecht moet komen (op de stack voor de return value, waarschijnlijk), en rekenen terug waar dat adres dan verder gebruikt kan worden. Je ziet dus dat de lhs direct gekopieerd wordt naar de locatie van de return value, en dan wordt er in-place rhs bij opgeteld.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • The Flying Dutchman
  • Registratie: Mei 2000
  • Laatst online: 29-07 21:57
.oisyn schreef op woensdag 09 juni 2010 @ 12:31:
other.d_teller * d_noemer is een temporary. Maar goed, uiteindelijk hebben we het natuurlijk nergens over. Het zijn twee fucking floats, en menig compiler zal de boel gewoon in registers houden :)
Je hebt gelijk, ik keek naar temporary's op Rationaal niveau, niet op lager niveau. Op de komende generatie AMD bulldozer cpu's kun je het met een stukje assembly vast ook nog wel weer herschrijven naar een MADD voor ultieme performance ;).

The Flying Dutchman


Acties:
  • 0 Henk 'm!

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
The Flying Dutchman schreef op woensdag 09 juni 2010 @ 12:42:Op de komende generatie AMD bulldozer cpu's kun je het met een stukje assembly vast ook nog wel weer herschrijven naar een MADD voor ultieme performance ;).
Ik hoop dat de compiler dat gewoon voor me doet.

Acties:
  • 0 Henk 'm!

Verwijderd

MSalters schreef op woensdag 09 juni 2010 @ 12:39:
[...]

Nee, niet met de huidige generatie compilers. Die kijken waar de output uiteindelijk terecht moet komen (op de stack voor de return value, waarschijnlijk), en rekenen terug waar dat adres dan verder gebruikt kan worden. Je ziet dus dat de lhs direct gekopieerd wordt naar de locatie van de return value, en dan wordt er in-place rhs bij opgeteld.
MSalters, die wiki pagina over RVO geeft aan dat een temporary object (op de stack) ook met het return object kan worden geoptimaliseerd, dus het hoeft niet persee lhs te zijn (dat bedoel ik niet als pun/kritiek!)
Dus het schrijven van
C++:
1
2
3
Rationaal temp(lhs);
temp += rhs;
return temp;

zou dan toch ook volledig optimaal moeten kunnen? (volgens de standaard.)

Andere vraag: als je een Rationaal constructor hebt met een int als argument, dan kun je toch 5+Rationaal(...) schijven? Waarom zou operator+ persee vrij moeten zijn?

Laatste vraag aan de expert: wat is het verschil tussen Rationaal const& rhs en const Rationaal& rhs?

Acties:
  • 0 Henk 'm!

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Verwijderd schreef op donderdag 10 juni 2010 @ 19:54:
zou dan toch ook volledig optimaal moeten kunnen? (volgens de standaard.)
Dan heb je een onnodige extra reference.
Andere vraag: als je een Rationaal constructor hebt met een int als argument, dan kun je toch 5+Rationaal(...) schijven? Waarom zou operator+ persee vrij moeten zijn?
5 is een int, de Rationaal::operator+ wordt nooit gevonden.
Laatste vraag aan de expert: wat is het verschil tussen Rationaal const& rhs en const Rationaal& rhs?
Geen.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

C++:
1
2
3
4
5
6
7
const X * //een pointer naar een const object
X const *

X * const //een const pointer naar een object

const X * const //een const pointer naar een const object
X const * const


Vuistregel, lees je type van rechts naar links, en dan gewoon de woorden in die volgorde neerzetten ;)

[ Voor 7% gewijzigd door MLM op 11-06-2010 11:15 . Reden: pointers van gemaakt, refs zijn impliciet const natuurlijk :( ]

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

"& const" mag niet. De reference zelf kun je sowieso niet aanpassen. Je had beter pointers kunnen gebruiken :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

.oisyn schreef op vrijdag 11 juni 2010 @ 10:34:
"& const" mag niet. De reference zelf kun je sowieso niet aanpassen. Je had beter pointers kunnen gebruiken :)
je hebt een punt.
volgens mij mocht het wel vroegah wel, als ik MSDN mag geloven (VC geeft wel een level1 warning (4227) dat ie de qualifier ignored, met als documentatie "Using qualifiers like const or volatile with C++ references is an outdated practice.")

maar goed, ik werk hier ook met een enigzins antieke compiler (VS2005), dus wellicht dat het in 2008 of 2010 of GCC ook echt niet meer kan.

Het volgende compiled dan ook (en returned 43)
C++:
1
2
3
4
5
6
7
8
int main()
{
    int foo = 42;
    int & const ref = foo;
    int bar = 43;
    ref = bar;
    return ref;
}

[ Voor 54% gewijzigd door MLM op 11-06-2010 11:25 ]

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

8.3.2/1
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (7.1.3) or of a template type argument (14.3), in which case the cv-qualifiers are ignored.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1