virtual 关键字使派生对象无法访问方法
我有这样的代码:
#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=static_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);
oSpec->showName();
o1->showName();
o2->showName();
o1->sayHello();
o2->sayGoodbye();
delete oSpec;
return 0;
}
当我运行它时,它会显示以下输出:
I'm super!
I'm super!
I'm super!
Hello
Goodbye
但是,如果我从 Super
类的声明中删除 virtual
关键字:
class Super{
public:
/*virtual*/ void showName();
};
输出将变得正确一:
I'm super!
I'm special1
I'm special2
Hello
Goodbye
那么,我的问题是,为什么virtual
关键字的存在使得指针o1
和o2
运行方法Super: :showName()
而不是Special1::showName()
还是 Special2::showName()
?
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=static_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()
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用
virtual
时,输出更接近正确,而使用它时则不正确(除非您确实想要)。强制转换不会更改对象的类型,只会更改指针的类型。它们仍然是Super
类型,因此应该运行Super::showName
。将一种指针类型转换为另一种指针类型不会更改所指向事物的类型。怎么可能呢?虚函数的全部要点是能够调用“通用”指针上的方法并获取正确的派生类方法。
为什么使用虚拟函数的典型例子是针对音乐家的。您可能有一个函数,通过在传递的每个
Musician *
上调用Play
方法,使整个管弦乐队演奏。对于Pianist
来说,必须调用Pianist::Play
。通常,编译器会在编译时确定要调用哪个函数——早期绑定。编译器确定知道的唯一信息是指针的类型。 virtual 关键字导致绑定在运行时后期发生,此时类成员的实际类型已知。
顺便说一句,您仍然可以通过使用范围覆盖来调用基类方法。例如
o1->Super::showName();
。事实上,你声称“正确”的结果是灾难性的。运行
Special1::showName()
时,this
指针指向非Special1
类型(或派生自该类型)的对象:未定义的行为很容易导致崩溃。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 typeSuper
, and so it'sSuper::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 everyMusician *
it gets passed. For aPianist
, that has to callPianist::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 thethis
pointer pointing to an object that is not of typeSpecial1
(or something derived from it) is undefined behavior and can easily lead to a crash.因为你的
main
中的代码是错误的。这些对象都说“我是超级”,因为对于它们来说(实际上,在后台),最派生的类型仍然是超级
。这就是它们被创造出来的样子。您的静态强制转换打破了所有规则,成为未定义的行为(任何事情都可能发生),因为您告诉编译器
o1
是Special1
,而事实上,它是 < em>不是Special1
的类型。当您创建一堆派生对象并将它们存储在保存指向基对象的指针的指针/容器中时,
虚拟
函数通常很有用。反之则不然。您想要创建Special1
和Special2
类型的指针,并将它们存储在指向super
的指针中。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 stillsuper
. 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 aSpecial1
, when in fact, it is not a type ofSpecial1
.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 typeSpecial1
andSpecial2
and store them in pointers tosuper
.在这两种情况下,您的强制转换 (
Special1 *o1=static_cast(oSpec);
) 都是未定义的行为,因此就语言而言,任何输出都是可以接受的。您获取了一个Super
类型的对象并对编译器撒谎,告诉它它实际上是一个派生类。在这种情况下,您仍然会得到父类 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 typeSuper
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 classSuper
that it really is.您的代码只是未定义。
试图解释它为什么以某种特定的方式工作是没有用的。
这里:
oSpec 指向 Super 类型的对象。
因此在这些陈述中。强制转换具有未定义的行为,因为 oSpec 指向的对象既不是 Special 1 也不是 Special 2)。
如果您使用dynamica_cast<> (就像你在创建类层次结构时应该做的那样)。你会发现结果是NULL。
这将表明两个指针都是 NULL。
Your code is just undefined.
Trying to reason why it works in a particular way is useless.
Here:
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).
If you use dynamica_cast<> (like you should when casting up the class hierarchy). You will find the result are NULL.
This will show that both pointers are NULL.