6

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

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

h 文件:

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 文件,则转换成功。

什么会导致它?

编译器是 gcc 3.1,在 Linux-SUSE 上。顺便说一句,我只在这个平台上看到这种行为,并且相同的代码在 Visual Studio 中运行良好。

4

6 回答 6

8

Base有虚函数吗?否则它将无法正常工作。如果不出意外,请将其 dtor 设为虚拟。

不知道是否已经被删除他的答案的其他人问过,但我相信这是不同的:你是从基地的构造函数做 dynamic_cast 吗?如果是这样,那就行不通了。编译器会认为 Base 是派生最多的类型,类似于调用虚函数并最终调用 Base 的版本。

于 2009-02-26T12:44:16.487 回答
6

如果您在基类中有一个虚函数(正如 litb 指出的那样),那么发布的代码应该不会失败。

但我相信,如果你没有,每个当前的编译器都会产生“基类不是多态”的错误,所以这可能不是问题。

我唯一能想到的是,由于一些奇怪的错误,所有内容都被内联并且没有生成任何 vtable。但是如果你将构造函数放在 C++ 文件中,编译器会决定不内联所有内容,从而触发 vtable 的创建,从而使你的强制转换工作。

但这是非常疯狂的猜测,我认为任何编译器都不会出现这样的错误(?)

如果您想要一个明确的答案,请发布更多代码。以及使用的编译器/平台。

编辑:查看更新的代码

我认为您至少应该从 Base 派生;)(我想这是一个错字)

但是在看到代码之后,我唯一能想到的是 gcc(错误地)内联了所有内容并且不会为 Derived 生成 vtable。对于它的价值,它运行良好,用 gcc 4.0 编译

3.1 到现在已经超过 7 年了......如果有任何升级的可能性,我会去的。

于 2009-02-26T13:21:03.060 回答
4

将析构函数设为虚拟,并将其(或至少一种虚拟方法)放在 .cpp 文件中。

一些编译器(阅读:gcc)寻找第一次遇到的非内联虚方法体并使用它来决定将虚方法表放在哪里。如果在 .cpp 文件中没有任何带有主体的虚拟方法,则不会创建虚拟方法表。

您必须至少有一种虚拟方法才能使 dynamic_cast 工作。动态转换使用表来计算类型信息,如果没有虚拟方法,则不会创建表。

如果你有一个你希望被子类化的类并且它有一个析构函数,或者如果这个类有任何实例变量是带有析构函数的类,那么你真的想让你的析构函数成为虚拟的(即使它有一个空的主体) . 否则,子类实例不会发生您期望的清理。

于 2009-02-26T13:27:19.100 回答
0

您是在 Visual C++ 中执行此操作吗?我认为您过去必须在编译器设置中启用运行时类型信息 (RTTI) 才能使其正常工作。

如果我错了,请不要喷我。好久没用C++了!!!

于 2009-02-26T13:35:55.880 回答
0

在查看您的代码时,我没有看到任何继承。你忘了这样做吗?派生不是从任何东西派生的。

于 2009-02-26T14:34:06.397 回答
0

在您发布的代码中,Derived 不是从 Base 派生的。

编辑: 仅供参考,修改后的代码适用于 g++ 3.4.5

于 2009-02-26T14:36:31.257 回答