编译并运行此代码时:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void doSomething()
{
cout << endl << "hi this is base";
}
};
class DerivedA : public Base
{
public:
void doSomething()
{
cout << endl << "hi this is derivedA";
}
};
class DerivedB : public Base
{
public:
void doSomething(){
cout << endl << "hi this is derivedB";
}
};
int main () {
DerivedB bb;
Base* cc;
cc = (DerivedA*) &bb;
cc->doSomething();
}
输出为hi this is derivedB"
。
现在这只是我做了严格违法的事情,我的编译器还是设法编译它?因为我会想象DerivedA
指针在调用DerivedB
时不会知道它指向doSomething
对象,因为那里没有Base类/派生类关系。
是否有一种资源可以比http://www.cplusplus.com/doc/tutorial/的教程更深入地解释C ++继承和多态的复杂性?
答案 0 :(得分:3)
cc = (DerivedA*) &bb;
丁丁丁!未定义的行为就在这里!
是的,完全违法。或者,不完全是非法的,但未定义的行为,这是非常非法的。现在这只是我做了严格违法的事情
回答问题标题:不,你不需要基类指针。但是,你不能只在不相关的类型之间进行投射,并希望它有效。
答案 1 :(得分:1)
DeriveA是Base,DeriveB是Base这两个派生自基类的类型,你可以使用Base指针调用DeriveA或DeriveB(polymorphysim),但你不能从子类型转到另一个
答案 2 :(得分:1)
正如Xeo指出的那样,您的代码是可编译的,但具有未定义的行为。您应该使用C ++的显式强制转换运算符 - static_cast
,dynamic_cast
,reinterpret_cast
和const_cast
来防止此类错误。
查看有关dynamic_cast
:http://www.cplusplus.com/doc/tutorial/typecasting/
答案 3 :(得分:0)
将指向DerivedB
对象的指针转换为DerivedA
对象,然后以你正在做的方式调用DerivedA
函数之一就是要求麻烦(并且基本上是未定义的)行为),因为DerivedB
不是从类型DerivedA
派生的。
就编译器而言,它并不是严格的“非法”,但你不应该这样做,如果你想从代码中获得可靠的行为,那么你不应该这样做。
如果你想在运行时进行额外的检查,你应该检查dynamic_cast
,而不是使用C风格的转换。
为什么它可能有效与vtable的布局方式有关。使用g ++,如果使用命令-fdump-class-hierarchy
进行编译,您将获得vtable的转储,如下所示:
Vtable for DerivedA
DerivedA::_ZTV8DerivedA: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI8DerivedA)
16 (int (*)(...))DerivedA::doSomething
...
Vtable for DerivedB
DerivedB::_ZTV8DerivedB: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI8DerivedB)
16 (int (*)(...))DerivedB::doSomething
如您所见,函数doSomething
对于类型DerivedA
和DerivedB
的两个实例都具有相同的偏移量。在这种情况下,指针指向DerivedB::doSomething
,这就是输出为"hi this is derivedB"
的原因。
答案 4 :(得分:0)
cc = (DerivedA*) &bb;
由于DerivedA
和DerivedB
与直接继承路径无关,因此您无法使用static_cast
从DerivedB*
转换为DerivedA*
。这意味着C样式转换等同于reinterpret_cast
。
虽然可以保证,如果将此reinterpret_cast
的结果转换回其原始类型,它将产生相同的指针值,您不能保证中间指针的值,结果没有具体说明。这意味着(DerivedA*)&bb
可能根本不指向有效对象,将此指针转换为Base*
(通过将其指定给cc
)的结果可能不会指向有效{{1} }。class。
由于您无法保证Base
的值,因此通过它调用成员函数可能会导致未定义的行为。