我有这段代码:
#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
但是,如果我从virtual
类的声明中删除Super
关键字:
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()
?
答案 0 :(得分:2)
输出更接近于virtual
更正并且不正确(除非你真的想要)。强制转换不会改变对象的类型,只会改变指针的类型。它们仍然是Super
类型,因此应该运行Super::showName
。
将一个指针类型转换为另一个指针类型不会更改指向的事物的类型。怎么可能呢?虚函数的重点是能够在'泛型'指针上调用方法并获得正确的派生类方法。
您使用虚拟功能的典型例子是音乐家。您可能有一个函数可以通过在传递的每个Play
上调用Musician *
方法来使整个管弦乐队演奏。对于Pianist
,必须致电Pianist::Play
。
通常,编译器会确定在编译时调用哪个函数 - 早期绑定。编译器必须知道的唯一信息是指针的类型。 virtual
关键字导致绑定在运行时更晚发生,此时类成员的实际类型已知。
顺便说一下,您仍然可以使用范围覆盖来调用基类方法。例如o1->Super::showName();
。
事实上,你声称“正确”的结果是灾难性的。使用Special1::showName()
指针指向不属于this
类型的对象(或从其派生的内容)运行Special1
是未定义的行为,很容易导致崩溃。
答案 1 :(得分:2)
因为main
中的代码错误。对象都说“我超级”,因为对于他们(实际上,在背景中),大多数派生类型仍然是super
。这就是他们被创造出来的。
你的静态强制转换破坏了所有规则,是未定义的行为(任何事情都可能发生),因为你告诉编译器o1
是Special1
,实际上它是不是一种Special1
。
virtual
函数通常很有用,并将它们存储在一个指针/容器中,该指针/容器包含指向 base 对象的指针。不是相反。您希望创建Special1
和Special2
类型的指针,并将它们存储在指向super
的指针中。
答案 2 :(得分:2)
你的演员表({{1}})在两种情况下都是未定义的行为,因此就语言而言,任何输出都是可以接受的。你已经使用了Special1 *o1=static_cast<Special1 *>(oSpec);
类型的对象并且对编译器撒了谎,告诉它它确实是一个派生类。在这种情况下,会发生的情况是您仍然得到父类Super
的结果。
答案 3 :(得分:1)
您的代码尚未定义。
试图解释为什么它以特定的方式工作是没用的。
下面:
Super *oSpec=new Super;
oSpec指向type
Super。
因此在这些陈述中。强制转换具有未定义的行为,因为oSpec指向的对象既不是Special 1也不是Special 2。
Special1 *o1=static_cast<Special1 *>(oSpec);
Special2 *o2=static_cast<Special2 *>(oSpec);
如果您使用dynamica_cast&lt;&gt; (就像你在编译类层次结构时一样)。你会发现结果是NULL。
Special1 *o1=dynamic_cast<Special1 *>(oSpec);
Special2 *o2=dynamic_cast<Special2 *>(oSpec);
std::cout << "O1(" << (void*)o1 << ") O2(" << (void*)o2 << ")\n";
这将显示两个指针都是NULL。