CS247 Lecture 2
Last Time: ADT design, explicit, MIL. This Time: MIL finished, operator overloading
In MIL, we give it the value immediately whereas in setting things in the constructor body, you have to default construct an object field. It takes time because it will have a method call, then you immediately overwrite it in the body.
- Using the MIL is considered better style than assigning the fields in the ctor body.
- Always use it.
There are cases where using the MIL is necessary:
- field id is const, need to use MIL
- References must always be initialized (have a number).
- Tries to default compile A, but it’s not in class B. So does not work.
- Stuck on step2: Since B inherits from A, it will want to store x too in itself, so it will want to initialize x but it’s not in the MIL of B. It will try to default construct it from inside class A. But there is no default constructor because we provided a constructor. Hence, it won’t compile.
- What if we gave A a default ctor?
Breaks down for a different reason. Step 2: Didn’t specify how to initialize in superclass, it goes to A and default constructs. Attempts to initialize x to 0. Step 3: fields are initialized, integer y is a primitive field, it will be left with a garbage value. Step 4: constructor body runs. Cannot set the value of this→x = x because x is a private field. Cannot set private fields in the subclass, because they are not accessible. Only public and protected fields.
Correct way: to initialize B with MIL
We specified that we wanted to initialize the A portion of this object using the value of x. When we are creating our B object, we need to initialize the A portion, we call the constructor taking in one argument, calls the constructor in A. All goes well. BUT since we don’t have default ctor for A, and you call when constructing in B A{} without arguments, it won’t compile.
The 4 Steps:
- Space is allocated (stack or heap).
- Call the superclass ctor.
- Initizalize fields via MIL.
- Run the ctor body.
Now let’s consider support Rational Operations:
Overloading
To support this functionality, we need overloading.
Example
Note
Cannot overload based solely on return type.
To perform an operator overload we define a function witht the name operator we define a function with the name operator concatenated with the operator symbol.
operator +, operator>>, operator!, operator==
The number of arguments, must match the arity of the operator.
Example
+: binary operator - 2 args
!: unary operator - 1 args
To support cin>>r>>q
; where r, q are Rationals. Define the following operator overload:
- cin is passed to
istream& in
, r is passed toRational& r
. - Why is
istream
passed by reference &- because copying is disallowed for istreams
- The Rational is passed by reference because we want changes to be reflected outside this function.
- Why
return in
?- We return in to allow for chaining.
cin >> r >> q
cin >> r
is evaluated first.- If we return in, it simplifies to
cin >> q
.
Problem
r.num
andr.denom
are private fields, cannot be accessed in the operator.
Solution 1: Provide accessor methods
- Provide methods
getNum
andgetDenom
which return references to the num and denom fields. - Sometimes paired with mutator methods
setNum
andsetDenom.
.- (Could enforce invariants like denom != 0) with these methods.
These are sometimes called getters/setters
Solution 2: Declare operator >>
as a Friend (C++)
Now, support p = q + r
adding two rationals together.
Define operator+
for two Rationals:
Take in arguments via constant reference:
- Constant - don’t want lhs or rhs to change.
- Reference - quick, no copying.
Declaring all these overloads as friends is a pain!
Alternative: Define operator overloads as methods in Rational.
- this takes the place of the lhs
r+q == r.operator+(q);
Note
operator<<
andoperator>>
are usually defined as standalone functions. Because cin/cout appear on the lhs.
What if we want r+5
?
What about 5+r
?
Order of args matters. We want an integer on the lhs, so we need a standalone function here:
What about p = q + r
?
Setting one Rational to another?
- Compiler provides a copy assignment operator for you.
- Can also write our own copy assignment operator:
- Why is the return type
Rational&
, why do we return * this? We can also chainoperator=
a=b=c=d=e;
Evaluates right to left, returns the value that was set.d=e
executes first. Returns reference to d. Simplifies toa=b=c=d
.
Next: CS247 Lecture 3 Links to this page CS247 - Software Engineering Principles