无法将超类转换为子类

5

我目前正在编写游戏和渲染引擎之间的抽象层。不幸的是,我遇到了一个问题:我似乎无法将超类(抽象接口)转换为子类(具体引擎的实现)。以下是我的代码:

IInitationSettings.h

class IInitationSettings {};

OxygineInitiationSettings.h

#include "IInitiationSettings.h"
#include "core/oxygine.h"
class OxygineInitiationSettings : public IInitationSettings, public oxygine::core::init_desc {
public:
    OxygineInitiationSettings(const char* title, bool vsync, bool fullscreen, int width, int height);
};

OxygineInitiationSettings.cpp

#include "OxygineInitiationSettings.h"
OxygineInitiationSettings::OxygineInitiationSettings(const char* title, bool vsync, bool fullscreen, int width, int height) : oxygine::core::init_desc() {
    this->title = title;
    this->vsync = vsync;
    this->fullscreen = fullscreen;
    this->w = width;
    this->h = height;
}

抽象的初始化方法:
static void init(IInitiationSettings& initSettings);
void GraphicsFactory::init(IInitiationSettings& initSettings){
#ifdef USE_OXYGINE_RENDERING
    OxygineInitiationSettings settings = initSettings; //Does not work
    oxygine::core::init_desc desc = initSettings; // Does not work
    oxygine::core::init((oxygine::core::init_desc)((OxygineInitiationSettings)initSettings)); //Does not work
#endif
}

我该如何将我的抽象接口转换为具体实现?我想添加一个新的newInitiationSettings方法,它将返回一个IInitiationSettings对象,我将把它传递给init方法,以便使代码更加简洁。 (我希望我的游戏内代码看起来像这样:)
GraphicsFactory::init(GraphicsFactory::newInitiationSettings(args));

有什么想法吗?


2
不确定你在问什么。如何使用 static_cast 或 dynamic cast?如何避免需要进行转换? - Ami Tavory
具体是什么出了问题?你能提供一些错误信息吗? - Mateusz Kacprzak
@AmiTavory 不,我在问为什么我不能将我的超类IInitationSettings转换为我的子类OxygineInitationSettings。 - Fly
@MateuszKacprzak 嗯,就是IntelliJ告诉我我所做的强制转换是不可能的。 - Fly
2个回答

11

这里的根本错误是在抽象的init方法中试图将对象本身转换为不同类型。向上转换(即朝基类方向)会导致对象切片,因为它只复制了基类的数据,通常这很糟糕,但向下转换则有可能无法实现。因此,编译器不会让你这样做。

你真正想要做的是在引用或指针级别工作。松散地说,引用是指针的语法糖,对象的指针可以替代它的基类的指针。这就是为什么你可以通过base&类型的参数传递一个derived。但是当你尝试获取你的derived时,你必须请求一个derived&derived*。在你的案例中,这更像以下这些:

static_cast<OxygineInitiationSettings&>(initSettings) // or
dynamic_cast<OxygineInitiationSettings&>(initSettings)

或者,如果您需要指针,也许是这样的:

static_cast<OxygineInitiationSettings*>(&initSettings) // or
dynamic_cast<OxygineInitiationSettings*>(&initSettings)

如果你确定initSettings将引用一个OxygineInitiationSettings实例,你应该使用static_cast而不是dynamic_cast。如果你不确定,你应该确保自己的判断或使用dynamic_cast而不是static_cast。请注意,动态引用转换会引发一个std::bad_cast异常,而动态指针转换将返回一个空指针,如果被initSettings引用的实际对象并不是一个OxygineInitiationSettings


谢谢您的回答,但不幸的是它并没有起作用。 IntelliJ 下划线标记了 static_cast 并显示 "Invalid type conversion"。 - Fly
没事了,它可以工作了,我在头部类中打错了“Initiation” =) - Fly

2

Dynamic_cast 可以从虚拟类(即具有虚拟方法的类)执行。只需像这样添加一个 dummy() 方法:

class IInitationSettings {
    virtual void dummy() {}
};

并且从隐式转换为动态转换:

void GraphicsFactory::init(IInitationSettings& initSettings) {
    OxygineInitiationSettings settings =
        dynamic_cast<OxygineInitiationSettings&>(initSettings); //Does indeed work
}

会解决问题。

1
谢谢,它起作用了!但是为什么我需要添加一个虚拟方法来告诉编译器这个类是抽象的?嗯,我更喜欢Java的接口和抽象类... - Fly
3
如果您在类型为基类时手动删除(或使用任何智能指针包装器)且基类没有虚析构函数,则语义上是不正确的。因此,您不需要一个虚拟方法。相反,请使用虚拟析构函数;这可能是您真正想要的。 - Nate

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