Reputation: 1222
This is a bit of code from http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators/
I have been looking into operator overloading from several self-teaching websites and browsing forums. I asked a question about overloading using a different method and I understand that now. That Q&A link is here How is this returning a value when it has not been defined?
However, this method with references I do not understand. I have tried to play with different ways to implement the code and have seen that some ways work that are less complicated.
The way I understand to do this is make an instance of a class as a parameter of the operator function, a temporary instance inside the operator function and return those values with an = operator. This way is much more complex and I have a couple of issues as to why this even works.
The questions are worded as you go down the code but here are the three questions I have.
(Question 1) Ceteris parabis, why do I need the const keywords in the parameters? I know that if I make the variables public I don't but why if there is a friend class, or if the code is written inside the class itself do I need to use const.
(Question 2) If I put the friend function inside the class, I still need the keyword "friend" why?
(Question 3) Where are the classes c1, and c2 initialized? There is a reference for them, but there is no initialization until the return, but that is below the reference. I would think there would be an error when compiling.
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents) { m_nCents = nCents; }
//I know this assigns each
//instance to the private variable m_nCents since it's private.
// Add Cents + Cents
friend Cents operator+(const Cents &c1, const Cents &c2);
//why do we need
//to make this a friend? why can't we just put it inside the class and not
//use the "friend" keyword? also why do I need to make the variables public
//if i remove const from the parameters
int GetCents() { return m_nCents; }
//I know how this is used to return the
// variable stored in m_nCents, in this program it is for cout
};
// note: this function is not a member function!
Cents operator+(const Cents &c1, const Cents &c2)
//where are these references
//actually defined? I do not see c1 or c2 anywhere except in the return, but
//that is below the code that is referencing the class
{
// use the Cents constructor and operator+(int, int)
return Cents(c1.m_nCents + c2.m_nCents);
}
int main()
{
Cents cCents1(6);
Cents cCents2(8);
Cents cCentsSum = cCents1 + cCents2;
std::cout << "I have " << cCentsSum .GetCents() << " cents." << std::endl;
return 0;
}
Upvotes: 1
Views: 235
Reputation: 56921
Maybe the following is more illustrative: When you calculate the sum, your code is equivalent to:
Cents cCentsSum = operator+(cCents1,cCents2);
(yes, the above is actual legal code as well).
This is more similar to a function call, which the operator in fact is. The references are the parameters, hence const Cents& c1
references cCents1
and const Cents& c2
references cCents2
.
This free function now needs to be implemented, but you are accessing c1.m_cents
, which is private
. In order to allow this, you need to declare the method a friend
, otherwise the free function you wrote would yield an access error.
You still need to declare it a friend
even if you put it inside the class because operators are special. The compiler sees that your operator + has two arguments, hence it knows that this is not a member function. It is actually equivalent to the code for the free function, just more compact.
Finally, you want those references to be const
to avoid accidential modification. Consider if you write:
Cents operator+(Cents &c1, Cents &c2)
{
return Cents(c1.m_nCents += c2.m_nCents);
}
Note the +=
instead of +
, it would modify the value of c1
. In your example, this would mean that after you called
Cents cCentsSum = cCents1+cCents2;
the value of cCents1
would have been modified. Certainly not desirable. By making the parameter const references, you ask the compiler to ensure that the above can't happen and is reported as an error when compiling the code.
Upvotes: 4
Reputation: 5938
The big bonus to const is it means they wont change, because with C++ the compiler can see all the types, if it is not mutable but const it knows that only const methods can be called and such, ad it can enforce that const methods not alter that state (when compiling the implementation of those classes)
When you use a const anything the compiler can be sure that it will not change (unless mutable, lets ignore that though) so it can make some nifty optimisations to the code.
If every function argument is const and not mutable then the compiler knows when it uses those functions that the object is in the same state before and after the call, so any values in registers for example need not be refreshed (although register allocation comes much later).
Const is good, if someone is a "total const whore" that's a compliment.
So to answer the question, private means that only friends and the class can alter the state, const means no-one can (unless mutable).
It's hard to give an example because optimisations only occur when the compiler can PROVE that following the/a standard the resulting code goes though the exact same program states as if it did what you wrote.
class State {
private:
bool state;
public:
State() {
state = false;
}
bool getState() {
return state;
}
void alterState() {
state = !state;
}
};
void doSomething(State&);
int main(int,char**) {
State state;
bool before = state.getState();
std::cout<<"The state is: "<<before<<"\n";
doSomething(state);
bool after = state.getState();
std::cout<<"The state is: "<<after<<"\n";
return 0;
}
And suppose doSomething is defined in another file.
When we find after the compiler must do a call to state.getState() as it cannot prove that before == after.
If however we have:
void doSomething(const State&);
int main(int,char**) {
State state;
bool before = state.getState();
std::cout<<"The state is: "<<before<<"\n";
doSomething(state);
bool after = state.getState();
/*compiler can do after=before because bool
is a type that will produce the same result
as if it actually did the getState*/
std::cout<<"The state is: "<<after<<"\n";
return 0;
}
This shows how const is great. The compiler knows that doSomething takes a constant (reference to a) State, and as state has no mutable fields it knows there's no way the State state could change because of that call. If they are all in the same file it may see what doSomething does (it doesn't change the state suppose) and realise it can do it anyway, modern ones are VERY very clever like that. You want to give the compiler every opportunity to optimise.
Passing about const references means everything can look but not touch, so any values the compiler has to hand are valid, because it's const they can't change.
Remember this: All optimisations work as if they never happened at all, the program state is the same, without a stop-watch or a debugger or disassembler you cannot tell what the compiler has done. It may optimise out some variables (if it can prove that has no side-effects), printing that variable though will stop it doing that, the compiler will always do exactly what you type, as per the standard.
Upvotes: 3