I have this code:
#include <iostream>
class Super{
public:
virtual void showName();
};
class Special1 : public Super {
public:
void showName();
void sayHello();
};
class Special2 : public Super {
public:
void showName();
void sayGoodbye();
};
void Super::showName() {
std::cout << "I'm super!" << std::endl;
}
void Special1::showName() {
std::cout << "I'm special1" << std::endl;
}
void Special1::sayHello() {
std::cout << "Hello" << std::endl;
}
void Special2::showName() {
std::cout << "I'm special2" << std::endl;
}
void Special2::sayGoodbye() {
std::cout << "Goodbye" << std::endl;
}
int main () {
Super *oSpec=new Super;
Special1 *o1=st开发者_运维百科atic_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);
oSpec->showName();
o1->showName();
o2->showName();
o1->sayHello();
o2->sayGoodbye();
delete oSpec;
return 0;
}
When I run it, it shows this output:
I'm super!
I'm super!
I'm super!
Hello
Goodbye
But, if I remove the virtual
keyword from the declaration of the Super
class:
class Super{
public:
/*virtual*/ void showName();
};
The output becomes the correct one:
I'm super!
I'm special1
I'm special2
Hello
Goodbye
Then, my question is, why the presence of the virtual
keyword makes the pointers o1
and o2
run the method Super::showName()
instead of Special1::showName()
or Special2::showName()
?
The output is closer to correct with virtual
and incorrect with it (unless you really wanted that). The cast doesn't change the type of the objects, just the type of the pointers. They are still of type Super
, and so it's Super::showName
that should run.
Casting one pointer type to another pointer type doesn't change the type of the thing pointed to. How could it? The whole point of virtual functions is to be able to call methods on 'generic' pointers and get the correct derived class method.
The classic example of why you use virtual functions is for musicians. You might have a function that causes the entire orchestra to play by calling the Play
method on every Musician *
it gets passed. For a Pianist
, that has to call Pianist::Play
.
Normally, the compiler figures out which function to call at compile time -- early binding. The only information the compiler is sure to know is the type of the pointer. The virtual
keyword causes the binding to occur late, at run time, when the actual type of the class member is known.
By the way, you can still call the base class method, by using a scope override. For example o1->Super::showName();
.
In fact, the result you claim is 'correct' is catastrophic. Running Special1::showName()
with the this
pointer pointing to an object that is not of type Special1
(or something derived from it) is undefined behavior and can easily lead to a crash.
Because your code in main
is wrong. The objects all say "I'm super" because for them (in reality, in the background), the most derived type is still super
. That's what they were created as.
Your static cast breaks all the rules, to be undefined behavior (anything can happen), because you tell the compiler that o1
is a Special1
, when in fact, it is not a type of Special1
.
virtual
functions are usually useful when you create a bunch of derived objects, and store them in a pointer/container that holds pointers to the base object. Not the other way around. You want to create pointers of type Special1
and Special2
and store them in pointers to super
.
Your cast (Special1 *o1=static_cast<Special1 *>(oSpec);
) is undefined behavior in both cases, so any output at all is acceptable as far as the language is concerned. You've taken an object of type Super
and lied to the compiler, telling it that it's really a derived class. In this case, what happens is that you still get the result of the parent class Super
that it really is.
Your code is just undefined.
Trying to reason why it works in a particular way is useless.
Here:
Super *oSpec=new Super;
oSpec points at an object of type
Super.
Thus in these statements. The casts have undefined behavior, as the object pointed at by oSpec is neither a Special 1 or a Special 2).
Special1 *o1=static_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);
If you use dynamica_cast<> (like you should when casting up the class hierarchy). You will find the result are NULL.
Special1 *o1=dynamic_cast<Special1 *>(oSpec);
Special2 *o2=dynamic_cast<Special2 *>(oSpec);
std::cout << "O1(" << (void*)o1 << ") O2(" << (void*)o2 << ")\n";
This will show that both pointers are NULL.
精彩评论