dynamic_cast 失败

7

我有一个基类和一个派生类,每个类都有一个.h文件和一个.cpp文件。

在以下代码中,我将基类对象动态转换为派生类:

头文件:

class Base
{
  public:
    Base();
    virtual ~Base();
};

class Derived : public Base
{
  public:
    Derived(){};
    void foo();
};

class Another
{
  public:
    Another(){};
    void bar(Base* pointerToBaseObject);
};

cpp文件:

Base::Base()
{
    //do something....
}
Base::~Base()
{
    //do something....
}
void Derived::foo()
{
    Another a;
    a.bar(this);
}
void Another::bar(Base* pointerToBaseObject)
{
    dynamic_cast<Derived*>(pointerToBaseObject)
}

出于某种奇怪的原因,强制类型转换失败(返回NULL)。但是,如果我将Derived类的构造函数的实现从.h文件移动到.cpp文件中,强制类型转换就会成功。

这可能是什么原因呢?

编译器是Linux-SUSE上的gcc 3.1。顺便说一下,在Visual Studio中,相同的代码运行良好,只有在这个平台上才会出现这种情况。


也许这是gcc 3.1的一个bug?尝试使用“-fdump-class-hierarchy”选项,并查看它是否为您的两个类创建了vtable。 - Johannes Schaub - litb
6个回答

9

你的Base类里有任何虚函数吗?如果没有,这段代码将无法正常工作。如果没有其他问题,请将它的析构函数设为虚函数。

不知道之前已经回答过这个问题的那个人是否提到了这点,但我认为这是一个不同的问题:你是在基类的构造函数中使用dynamic_cast吗?如果是,那么这样做是行不通的。编译器会认为Base类是最终派生类型,这和调用虚函数时最终也只会调用Base类版本的情况类似。


我在基类中确实有虚函数。 - Igor

6
只要你在基类中有一个虚函数,按照代码贴出来的样子,它不应该失败(正如litb所指出的那样)。
但我相信,如果你没有这样做,每个现代编译器都会生成一种“基类不是多态”的错误,所以这可能不是问题所在。
我能想到的唯一一件事是,由于某些奇怪的bug,所有东西都被内联,没有vtable被生成。但如果把构造函数放在C++文件中,编译器就决定不把所有东西内联起来,从而触发vtable的创建,使您的转换起作用。
但这只是极端猜测,并且我认为任何编译器都不会犯这样的错误(?)
如果您想获得明确的答案,请发布更多代码和使用的编译器/平台。
编辑:看到更新后的代码
我认为你至少应该将Derived从Base派生出来;)(我想这是一个笔误)
但看完代码,我唯一能想到的是gcc(错误地)内联了所有内容,并且没有为Derived生成vtable。就我所知,这在使用gcc 4.0编译时运行良好。
现在3.1已经超过7年了......如果有升级的可能性,我建议去做。

4

将析构函数声明为虚函数,并将其(或至少一个虚函数)放在.cpp文件中。

一些编译器(比如gcc)会查找第一个遇到的非内联虚函数体并使用它来决定虚方法表的位置。如果你没有任何在.cpp文件中带有函数体的虚函数,则不会创建虚方法表。

你必须至少拥有一个虚函数才能使dynamic_cast工作。Dynamic cast使用表格来确定类型信息,如果没有虚函数,则不会创建表格。

如果你有一个预期会被子类化的类,并且它具有析构函数或者该类具有任何带有析构函数的实例变量,则你确实需要将析构函数声明为虚函数(即使它具有空函数体)。否则,你期望的清理操作将不会对子类实例发生。


0
你是在使用Visual C++吗?我认为你需要在编译器设置中启用运行时类型信息(RTTI)才能使其工作。
如果我理解有误,请不要对我发火。我已经有一段时间没有使用C++了!

0
看了你的代码,我没有看到任何继承。你是不是忘了做这个?Derived 没有从任何地方派生出来。

0
在你发布的代码中,Derived没有从Base派生。
编辑:FYI,修改后的代码在g++ 3.4.5上可以正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接