Reputation: 57
#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = (Class1*)&object2;
p->f();//possibly abnormal program termination. but in fact, it will call function h(),Why?
return 0;
}
The code must be wrong in theory because p->f()
there is no function f() in the Class2.
But in fact, the code can be run, and it will call Function h() in the Class2, It's so strange,why??
Upvotes: 0
Views: 93
Reputation: 123084
The code must be wrong in theory because p->f() there is no function f() in the Class2.
The code is not only wrong in theory, but it is wrong in pratice too. The thing is that C++ does not hold your hand and does not wrap you in cotton wool, if you do wrong stuff, bad things can happen. Typesafety is your friend, but if you betray it, you are on your own.
In this line:
p = (Class1*)&object2;
you pretend that you could cast a Class2
to a Class1
. Anything beyond that is undefined behaviour, which is sloopy speaking a way of saying: c++ does not care what is the output of programs that dont obey the rules.
The way to stay friends with the typesystem is to use static_cast
instead of a C-style cast, which would tell you that the cast is not allowed.
Upvotes: 1
Reputation: 1613
There is a problem with the code, and as @tkausi commented, the behavior is undefined.
The problem is that you are using C-style cast which is dangerous and should be avoided in C++. From cppreference:
When the C-style cast expression is encountered, the compiler attempts to interpret it as the following cast expressions, in this order:
const_cast<new_type>(expression);
static_cast<new_type>(expression)
, with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;static_cast
(with extensions) followed byconst_cast
;reinterpret_cast<new_type>(expression);
reinterpret_cast
followed byconst_cast
.The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled (see example). If the cast can be interpreted in more than one way as
static_cast
followed by aconst_cast
, it cannot be compiled. In addition, C-style cast notation is allowed to cast from, to, and between pointers to incomplete class type. If both expression andnew_type
are pointers to incomplete class types, it's unspecified whetherstatic_cast
orreinterpret_cast
gets selected.
Thus, in your case the compiler will select reinterpret_cast
which will just reinterpret the raw bytes of your object as something else (and it just happens that the first thing in the virtual table of p
points to method h
).
To make your program safe, you should instead use static_cast
or dynamic_cast
:
#include<iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
};
class Class2 {
public:
virtual void h() {
cout << "Function h() in Class2\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
p = &object1;
p = static_cast<Class1*>(&object2); // fails to compile
p->f();
return 0;
}
In this case, compilation fails as expected. Live example: https://godbolt.org/g/noErNr
Note that if we replace static_cast
line with p = dynamic_cast<Class1*>(&object2);
the code will compile, but p
will be set to nullptr
at runtime, and trying to call p->f()
will result in an error.
Upvotes: 2
Reputation: 304
C++ doesn't have many run-time checks for performance reasons, so it will just do what you tell it to do even if it is illogical.
You are telling it to call the virtual method Class1::f
on a pointer organised in the memory layout of Class2
and apparently Class2::h
aligns with Class1::f
so it gets called.
This is not standardised behaviour and may differ between compilers. See Virtual method table.
Upvotes: 3