1- A function should be declared as virtual if we want to allow other contracts that inherit from ours to override that particular functions implementation.
2- A function should have the keyword override if the intent is to override the implementation of an inherited function with the same signature.
3- A function can have both override and virtual keywords in case it is overriding an inherited implementation but we still want to allow it to be overriden by contracts that inherit from ours.