Your code does have undefined behaviour. But let’s start at the beginning.
Sample s1 = new Sample(10);
This is what happens in this line:
- A
Sample
object is allocated on the heap and thenew
expression returns a pointer to it, aSample*
. - You cannot assign a
Sample*
to a variable of typeSample
. ButSample
has a constructor that allows implicit construction from an int. If you use the-fpermissive
compiler option (hint: don’t!), the compiler allows implicit conversion of a pointer to an integer – after all, a pointer is just a memory address, a.k.a. a number. - Accordingly
s1
is constructed by interpreting the memory address of the heapSample
object as an integer (truncating it ifsizeof(Sample*) > sizeof(int)
). That’s the value that ends up as*(s1.ptr)
.
To reiterate the key point: In that line you don’t instantiate one Sample
object, but two. Bug 1: The one created on the heap is never deleted. That’s a memory leak.
SomeFunc(s1);
Sample
has nothing in it that prevents the compiler from generating the default copy constructor and default copy assignment operator. Important: “default” for pointers means to copy the pointer, not the object behind it. So:
s1
is copied to callSomeFunc()
. The copy is available asx
in the function. Because of the default pointer copy boths1
andx
point to the sameint
object.x
goes out of scope at the end of the function, the destructor runs and deletes theint
object.
We are not quite undefined yet, but we’re getting close.
s1.PrintVal();
The function tries to acces the int
object behind the pointer, but it’s already deleted. s1.ptr
is a dangling pointer. Bug 2: Dereferencing a dangling pointer is undefined behaviour.
And all that because of that seemingly innocent implicit pointer-to-int conversion … That’s why it is a compiler error by default, at least in non-ancient compilers.
solved Getting undefined behavior for something that shouldn’t be getting undefined behavior