当使用指针使用具有多态性的函数时,指针必须是基类吗?

时间:2011-12-30 08:32:30

标签: c++ inheritance pointers polymorphism

编译并运行此代码时:

#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 ++继承和多态的复杂性?

5 个答案:

答案 0 :(得分:3)

cc = (DerivedA*) &bb;
丁丁丁!未定义的行为就在这里!

  

现在这只是我做了严格违法的事情

是的,完全违法。或者,不完全是非法的,但未定义的行为,这是非常非法的。

回答问题标题:不,你不需要基类指针。但是,你不能只在不相关的类型之间进行投射,并希望它有效。

答案 1 :(得分:1)

DeriveA是Base,DeriveB是Base这两个派生自基类的类型,你可以使用Base指针调用DeriveA或DeriveB(polymorphysim),但你不能从子类型转到另一个

答案 2 :(得分:1)

正如Xeo指出的那样,您的代码是可编译的,但具有未定义的行为。您应该使用C ++的显式强制转换运算符 - static_castdynamic_castreinterpret_castconst_cast来防止此类错误。

查看有关dynamic_casthttp://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对于类型DerivedADerivedB的两个实例都具有相同的偏移量。在这种情况下,指针指向DerivedB::doSomething,这就是输出为"hi this is derivedB"的原因。

答案 4 :(得分:0)

cc = (DerivedA*) &bb;

由于DerivedADerivedB与直接继承路径无关,因此您无法使用static_castDerivedB*转换为DerivedA*。这意味着C样式转换等同于reinterpret_cast

虽然可以保证,如果将此reinterpret_cast的结果转换回其原始类型,它将产生相同的指针值,您不能保证中间指针的值,结果没有具体说明。这意味着(DerivedA*)&bb可能根本不指向有效对象,将此指针转换为Base*(通过将其指定给cc)的结果可能不会指向有效{{1} }。class。

由于您无法保证Base的值,因此通过它调用成员函数可能会导致未定义的行为