You are on page 1of 7

Clearly the first line just give F, because of short-circuit evaluation:

As the first one is False, no need to continue (short)

When operators such as && , || are overloaded, they lose their special properties of
short-circuit evaluation and sequencing.

This means all of them will be evaluated

Tip: DON'T override both && , || .

They will be misused. Programmers forget or don't know the short-circuit evaluation
case for operator overloading.

Tip: Operator overloading is great till you support it unwisely!

In basic mathematics, exponentiation is resolved before basic arithmetic, so 4 + 3 2

resolves as 4 + (3 ) = 4 + 9 = 13
2

However, in C++, the arithmetic operators have higher precedence than operator^ , so
4 + 3 ^ 2 resolves as (4 + 3) ^ 2 => 7 ^ 2 => 49 .

You’d need to explicitly parenthesize the exponent portion

4 + (3 ^ 2) every time you used it for this to work properly, which isn’t intuitive, and
is potentially error-prone.

Because of this precedence issue, it’s generally a good idea to use operators only in an
analogous way to their original intent.

Rule: When overloading operators, it’s best to keep the function of the operators as
close to the original intent of the operators as possible.

https://www.learncpp.com/cpp-tutorial/91-introduction-to-operator-overloading/

Fraction

Original Src: https://en.cppreference.com/w/cpp/language/operators

Observe that: copy constructors and operators can directly access private variables of
another object of same class.

 https://stackoverflow.com/questions/4117002/why-can-i-access-private-variables-
in-the-copy-constructor

Code Reusability
○ The ability to reuse the existing coding efforts for a new function instead of
duplicating efforts.

OO has 2 reusability approaches: Inheritance and Composition

which Allows building hierarchy of classes and relations

Composition: has-a relationship

A car has-an engine and has 4-wheels

Inheritance: is-a relationship

Manager is-an employee

Multiple inheritance might inherit a lot of members that we don’t need

● Inheritance is a design-time you choice

● Code is built and compiled against specific class

○ Protected members + public interface

○ This dependency (coupling) is not good

● What if we need more dynamic behaviour during run time!

○ Composition + interfaces + Polymorphism: Dynamic + less coupling

Inheritance in practice

● 3 types of programmers

○ Never use it. Hated

○ Avoid as much as possible, especially multiple inheritance. Have STRONG


justifications

○ Those who don’t study basics well

● Tips:

○ Prefer composition over inheritance.

You typically can find a way

○ Be aware of inheritance problems.

○ Parent class is superclass for all possible relevant subclasses.

○ Don’t do it just to reuse some code.

○ Don’t extend a base class that was not designed for extension
● Reference and pointer derived object:

Virtual function: call on actual derived object (run-time / dynamic binding)

Once declared virtual, any derived is virtual, even if keyword not used

Non-Virtual: call based on the the used pointer type (compile-time / static binding)

No pointer: e.g. shape.draw = member selection operator = static binding

● Pure Virtual Function

○ Incomplete definition ⇒ can’t create objects. Same for subclass without


implementation.

■ Can’t also use as a function parameter or return as object, but can as pointer

○ Don’t call pure function from its class constructor. Undefined.

Polymorphism: Generic code

● The major goal of polymorphism is allowing more generic code

○ Editor is processing many shapes without knowing their details.

○ More types can be added in future with minimal code changes

● Polymorphism should help you build loosely coupled system

● Downcasting

○ Double check your design. These if/else are unhealthy

○ Static cast if confident. Otherwise: Dynamic cast and check the returned pointer

● Work more with interfaces, sometimes Abstract classes and much less with multiple
inheritance ⇒ avoids inheritance troubles

Interfaces: Guidelines
● Interface = contract. Don’t break it.

○ Changes may cause compilation errors. Consider backward compatibility.

○ Think deeply about method signature

● A minimal public interface

○ Doesn’t include your common or private functions

○ Avoid irrelevant functionalities / hard to get

● Principle of Least Surprise

○ Most guys don’t read documentation.

Expected resulting behaviour = match function name

Interfaces as properties

● In inheritance you think: Employee is a person

●With Interfaces: you might think also in properties as parent class

○ E.g. a class is Printable, Diskable(Savable, Loadable),

Clonable, Comprable, etc

○ Each one of the properties is an Abstract class with

relevant functionalities

○ A child class extends them to have/use these properties

● If a class might be inherited from several paths

○ Using Class child : virtual public parent { };

Polymorphism: Misc

● Global? Static? Friend? Can’t be virtual functions

● Virtual Tables

○ Extra pointer in base class + table per class: #rows = #virtual functions

○ Computed In compile time: When compiling we know all classes and their
relationships

● Build your own Interfaces/Abstract Classes to wrap APIs

○ Don’t depend on external APIs code. Polymorphism helps reducing dependency

● Framework uses Inversion of Control: Your function/class will be called in

specific time part of some cycle. Polymorphism may help designing that

● Templates: Behaviour doesn’t depend on data type. Polymorphism depends.

● Cohesion: how complete and related things are in a class

○ Take irrelevant things/behaviour outside the class

○ Cohesion is the indication of the relationship within a class/module.

○ Coupling is the indication of the relationships between classes/modules

● Users of a class must be dependent on its public interface,

○ but a class should not be dependent on its users

● Keep related data and behavior in one place.

● Make sure every class has a single responsibility: single reason to change

● Depends on interfaces not on other classes to reduce coupling

○ Less code changes in future

Parent class: design intention messages

● A non-virtual function

○ This is mandatory implementation. Don’t override. It won’t be called in


polymorphism.

○ You better mark also with final in the parent class

● A virtual function

○ You have a default implementation common between all subclasses

○ Mark with virtual and override in children classes

● A pure virtual function

○ I have no idea how to implement (e.g. Area() in shape class)

○ Or, I am acting like an interface function

Composition over Inheritance

● In inheritance: we highlighted several issues in (multiple) inheritance

● Composition with interfaces is the way to go

○ Dynamic vs Compile, protecting internals, access control, enforce encapsulation

○ You avoid the “combination hell” of inheritance (RobotDog)

● Design patterns are typically needed

○ Delegate pattern to switch B inherits A to ⇒ B has delegate of A

○ Switch template pattern to strategy pattern

○ Decorator pattern to avoid the Exploding class hierarchy

● Cons

○ More code to do delegation of calls on composed objects

■ You may need to wrap/delegate some classes to remove irrelevant functions


○ More code to create instances of intermediate classes ⇒ use Factory Pattern
Operators

● Can be overloaded:

○ + - / % ^ & | ~ !, = = ++ -- == != && || += -= /=

%= ^= &= |= = = [] () -> -> new new [] delete delete []

● Can be overloaded only as member function

○ [] = -> ()

● Operators can’t be overloaded

○ . . :: ?: sizeof

Operators: Member vs non-member functions?

● For member functions, the object must be on left!

○ This is very limited

● Non member functions allow more use cases

○ We can make them friend for faster access

○ Point of view: improve encapsulation

● Your approach

○ Either: Define as much possible as non-member function

○ Or: Define as possible as member functions + use non-member for remaining cases

● Extensibility

○ As you see, operator overloading allows adding extra functionalities for a class

○ This is an example of extensibility, an OOP feature

Operators: Order of evaluation

● Recall order for operators evaluation or might be unspecified

○ E.g. f1() + f2() + f3(). There are 6 ways of evaluating this expression

● Since C++17:

○ Evaluation of overloaded operators are now sequenced in the same way as for built-in

operators

○ the <<, >> and subscripting operators now have the left operand sequenced before the

right

■ Before 17: cout<<f1()<<” “<<f2(); ⇒ no guarantee f1() is called first

Operators Summary

● Restrict yourself to:

○ Using operator in similar meaning to the original semantic of the operator

○ Make sure it is intuitive to others and no need to checkout documentation

○ There is a real need and it makes sense (+ in strings, [] in vectors)

● Be aware of the precedence rules of operators ( 1 + 2^4)

● Common ones == = < << >> + [Don’t || && ^ and probably many others]

● With great power comes great responsibility

○ Think twice in what you are operator overloading. Don’t shoot yourself in the foot

● Investigating code written by others

○ Seeing i = j * 2. What really happens here needs some investigation to data


types/conversions

○ So one may need more code tracing & debugging. Don’t make people life harder.

● Popular cases: BigInteger, Fraction, Complex, Matrix, Vector

Operators Summary

● Operator overloading is another type of compile-time polymorphism

● Properties

○ Extensibility

○ Easier to read code & use classes

● Precedence / associativity / arity can’t be changed

○ Arity: Some operators are unary, others are binary and some both

● Has created pointers?

○ Provide destructor, copy constructor and operator=

● Reading

● Some languages, e.g. Java, don’t provide operator overloading

You might also like