Your code example invokes undefined behaviour, because you try to read from the int
variable n
while it is not in a valid status. The question is not what value will be printed. Your program is not required to print anything, or do anything that makes sense, although you are likely using a machine on which the undefined behaviour will only present itself as a seeminly random value in n
or on which it will mostly appear as 0.
Your compiler likely gives you an important hint if you allow it to detect such problems, for example:
34:21: warning: 'mynum.Number::n' is used uninitialized in this function [-Wuninitialized]
However, the undefined behaviour starts even before that. Here's how it happens, step by step:
UndoableNumber mynum;
This also creates the Number
sub-object with an unintialised n
. That n
is of type int
and can thus have its individual bits set to a so-called trap representation.
mynum.set(42);
This calls the derived-class set
function. Inside of set
, an attempt is made to set the before
member variable to the uninitialised n
value with the possible trap representation:
void set(T v) { before = BASE::get(); BASE::set(v); }
But you cannot safely do that. The before = BASE::get()
part is already wrong, because Base::get()
copies the int
with the possible trap representation. This is already undefined behaviour.
Which means that from this point on, C++ as a programming language no longer defines what will happen. Reasoning about the rest of your program is moot.
Still, let's assume for a moment that the copy would be fine. What else would happen afterwards?
Base::set
is called, setting n
to a valid value. before
remains in its previous invalid status.
Now foo
is called:
void foo(Number *n)
{
n->set(84); //Which function is called here?
}
The base-class set
is called because n
is of type Number*
and set
is non-virtual.
set
happily sets the n
member variable to 84. The derived-class before
remains invalid.
Now the undo
function is called and does the following:
BASE::set(before);
After this assignment, n
is no longer 84 but is set to the invalid before
value.
And finally...
cout << mynum.get() << '\n';
get
returns the invalid value. You try to print it. This will yield unspecified results even on a machine which does not have trap representation for int
s (you are very likely using such a machine).
Conclusion:
C++ as a language does not define what your program does. It may print something, print nothing, crash or do whatever it feels like, all because you copy an unininitialised int
.
In practice, crashing or doing whatever it feels like is unlikely on a typical end-user machine, but it's still undefined what will be printed.
If you want your derived-class set
to be called when invoked on a Number*
, then you must make set
a virtual
function in Number
.