在类库开发中使用基类中的派生方法 我正在编写一个小游戏引擎(它被用作一个库),同时把自己重新读入C++中。我已经用java编码了几年,但是C++看起来处理多态性有点不同,我遇到了以下问题:

在类库开发中使用基类中的派生方法 我正在编写一个小游戏引擎(它被用作一个库),同时把自己重新读入C++中。我已经用java编码了几年,但是C++看起来处理多态性有点不同,我遇到了以下问题:,c++,gcc,polymorphism,C++,Gcc,Polymorphism,我有一个类GameObject,它代表游戏中任何类型对象的基本功能。这基本上是作为一个抽象类使用的。 我有几个对象,它们覆盖了游戏对象,包括一个类映射。这些对象最终在游戏中使用 所有这些都被编译到一个库中,供游戏使用 以下是我使用的基本代码: gameobject.h: class GameObject { public: void loadFromJson(nlohmann::json& json); void load(); // [.

我有一个类GameObject,它代表游戏中任何类型对象的基本功能。这基本上是作为一个抽象类使用的。 我有几个对象,它们覆盖了游戏对象,包括一个类映射。这些对象最终在游戏中使用

所有这些都被编译到一个库中,供游戏使用

以下是我使用的基本代码: gameobject.h:

class GameObject {
    public:
        void loadFromJson(nlohmann::json& json);
        void load();
    // [...] not relevant
}
gameobject.cpp:

void GameObject::loadFromJson(nlohmann::json& json) {
    // [...] not relevant
    std::cout << "base-class function" << std::endl;
}

void GameObject::load() {
    // [...] not relevant
    this->loadFromJson(json);
}
map.cpp:

void Map::loadFromJson(nlohmann::json& json) {
    // [...] not relevant
    GameObject::loadFromJson(json);
    std::cout << "derived class function" << std::endl;
}
我不确定makefile中的哪些参数是相关的,因此我只是简单地粘贴我使用的所有参数,包括库,它们应该是无关的: makefile,gameengine(称为gct):

makefile,游戏:

COMPARGS=-Wall -std=c++17 -fsanitize=address -I$(LIBINCLUDE) -lgct -lplibfnt -lplibul $(SDL_FLAGS) $(PKG_FLAGS) -lSOIL -lstdc++fs -g

testgame: $(SRCPATH)main.cpp [...]
$(CC) $(SRCPATH)main.cpp [...] $(COMPARGS) -o testgame
现在,我的预期行为将是输出: “基类函数” “派生类函数”

但我得到的是: “基类函数”

我在库中使用多态性时已经遇到了问题,loadFromJson()在基类中是虚拟的。并将其用于一系列游戏对象(当时是用于其他类和其他方法,但其背后的原理应该变得清晰)。这导致了奇怪的错误,谷歌解释说这种多态性很慢,不应该在库中使用。我知道,这种多态性的方式应该没有太大区别,但我没有发现任何错误。只是不是预期的行为,我觉得这应该有效

然而,对于我能够解决的其他类和其他方法,这是一个不同的问题。但如果我不能使用它,这将是非常令人不安的,因为我必须在派生类中反复重写load()。 我发现其他帖子在多态性方面有(相对而言)类似的问题,但答案总是代表了我的预期行为。
所以我的问题是:有什么可能导致它不能像我希望的那样工作,或者更准确地说:这可能是将代码用作共享库的结果吗?也许代码中有一个错误我找不到…我希望并认为,我给你的代码应该足够了。我怀疑,这是别的。但是,如果问题在其他地方,我会很高兴可能会导致一些错误。

< P>区别在于java方法是默认的C++方法,你需要用“代码>虚拟< /Cord>>关键字来显式地标记任何想要成为虚拟的方法。
class GameObject {
    public:
        virtual void loadFromJson(nlohmann::json& json);
        void load();
    // [...] not relevant
}
最好使用
重写标记派生方法,这样如果方法未重写基类方法,编译器将引发错误:

class Map : public GameObject {
    public:
        void loadFromJson(nlohmann::json& json) override;
        // [...] not relevant
}
这将始终调用
GameObject::loadFromJson
,即使使用
loadFromJson
方法声明子类
Map

在Java中,
Map
的一个实例将导致
Map
loadFromJson
()从
load()
调用。但是,C++不是java,普通类方法默认不会从子类重写。

要使其可重写,必须在基类中使用
virtual
关键字:

class GameObject {
    public:
        virtual void loadFromJson(nlohmann::json& json);
        void load();
    // [...] not relevant
};
这将使此方法的工作方式与Java中等效代码的工作方式类似,在本例中,
Map
的方法现在将被调用

此外,这不是必需的,但是您也应该使用当前的C++标准:

,用<代码>重写< /Cord>>关键字,将子类方法显式声明为<代码>虚拟<代码> >
class Map : public GameObject {
    public:
        virtual void loadFromJson(nlohmann::json& json) override;
        // [...] not relevant
};
<>这并不需要实现虚拟方法调度,但这将有助于C++编译器在将来对您犯错误或键入时大叫。在基类中添加参数或更改方法的某些内容是很常见的,但在子类中却忘记了这样做。如果发生编译错误,将子类方法标记为实际重写的方法将导致编译错误,并提醒您该问题


<> P>关于虚拟方法的更多信息以及如何正确地重写它们(C++中你可以根据方法来控制),请参阅C++书籍中的相关章节。

“这引起了奇怪的错误”——我敢打赌,这些奇怪的错误是由于你的谷歌搜索把你送进了错误的兔子洞。不管怎样,您想要的行为是锁定库存和两个桶
loadFromJson
GameObject
中至少应该是虚拟的,如果不是纯虚拟的。并且在任何派生中都应该
覆盖
属性。如果不使用方法
虚拟
,您就不会得到任何多态性,因此
this->loadFromJson(json)
需要调用
GameObject::loadFromJson
。例如使用智能指针、向量容器和适当的多态性。啊,好的!你能快速解释一下虚拟关键词的含义吗?我不知道为什么,但我一直认为它是用于空方法的…Tbh对我来说是相当愚蠢的…因为这样会有4个或类似的不同变体如何在c++中声明空方法你必须引用“纯虚拟方法”,这将是一个类似于“
virtual void loadFromJson”的声明(nlohmann::json&json)=0;
”表示此方法根本没有在此基类中定义,此基类不能直接实例化,只能在子类中实例化,并且必须在每个实例化的子类中定义它。否则,它的确切含义是:此方法可在定义此方法的子类中重写,该子类使用相同的签名。否则,子类方法并没有覆盖它。还有很多,更多信息见你的C++书籍。
class Map : public GameObject {
    public:
        void loadFromJson(nlohmann::json& json) override;
        // [...] not relevant
}
void GameObject::load() {
    // [...] not relevant
    this->loadFromJson(json);
}
class GameObject {
    public:
        virtual void loadFromJson(nlohmann::json& json);
        void load();
    // [...] not relevant
};
class Map : public GameObject {
    public:
        virtual void loadFromJson(nlohmann::json& json) override;
        // [...] not relevant
};