相互依赖的头文件和 class/methods 无法编译

Interdependent header files and class/methods can't compile

我目前正在构建一个小型 ECS 系统,在此过程中,我创建了一个 Application.h 和 Gameobject.h,其中 Gameobject.h 文件需要同时声明应用程序 class 及其一些方法,而 Application.h 需要游戏对象 class 及其一些方法。我尝试使用两个 classes 的前向声明,但是在获取尚未定义的 class 方法时这无济于事,因为前向声明没有定义 class 方法。

这是Gameobject.h:

    #pragma once

#include "Common.h"

class Gameobject
{
    friend class Application;

public:
    Gameobject(std::string name)
        :name(name) 
    {
        Application::Get()->AddGameobject(this);
    }

    std::string name;

    template<typename TComponent>
    void AddComponent()
    {
        DERIVES_FROM_COMPONENT;

        TComponent* component = new TComponent();
        component->SetGameobject(this);

        m_Components.push_back((Component*)&component);
    }   
    
    template<typename TComponent>
    void RemoveComponent()
    {
        for (const Component& component : m_Components)
        {
            if (typeid(component) == TComponent)
            {
                m_Components.erase(component);
                delete component; 
                
                return;
            }
        }
    }

private:

    void OnUpdate()
    {
        for (Component* component : m_Components)
        {
            component->OnUpdate();
        }
    }

    std::vector<Component*> m_Components;
};

和Appplication.h:

#pragma once

#include "Common.h"

class Application
{
    friend class Gameobject;

public:

    static Application* Get()
    {
        //make a unique pointer later
        static Application* s_Instance = new Application();

        return s_Instance;
    }

    void Run()
    {
        std::cout << "Running";

        while (isRunning)
        {
            for (Gameobject* gameobject : m_Gameobjects)
            {
                gameobject->OnUpdate();
            }
        }
    }

    bool Close()
    {
        return true;
    }

    bool isRunning = true;

private:

    Application() {};

    ~Application() { delete Get(); }

    void AddGameobject(Gameobject* gameobject)
    {
        m_Gameobjects.push_back(gameobject);
    }

    void RemoveGameobject(Gameobject* gameobject)
    {
        const auto& it = std::find(m_Gameobjects.begin(), m_Gameobjects.end(), gameobject);
        m_Gameobjects.erase(it);
    }

    std::vector<Gameobject*> m_Gameobjects;
};

然后 Common.h:

#pragma once

#include <iostream>
#include <vector>

class Gameobject;

#include "ECS.h"
#include "Components.h"

#include "Application.h"
#include "Gameobject.h"

因此游戏对象构造函数需要 application.AddGameobject 方法并且 Application.Run() 需要 gameobject.OnUpdate()

我对 C++ 还是比较陌生,所以我是否遗漏了一些可以使链接和编译更容易或者只是解决问题的东西?

-谢谢

这是来自 Java(可能还有 C#)背景的人常犯的错误。 C++ 很少在头文件中定义 class 方法,并且只有 如果它们不需要任何其他依赖项,除了 class 本身 -- 也许他们可以使用 #included 其他东西,但这是离题了。无论如何,在你的 GameObject.h 头文件中:

Gameobject(std::string name)
    :name(name) 
{
    Application::Get()->AddGameobject(this);
}

为了用 C++ 编译它,Applicationclass 需要 完全定义。除非 class 被完全定义,否则您的 C++ 编译器不知道 AddGameObject 是什么。这是一些神秘的方法 Get() returns,也没有在任何地方定义。这对你的 C++ 编译器来说是一个很大的谜,它正在编译这个头文件并且除了它的名字之外没有关于 Application class 的其他信息。

为此,Application 的头文件当然可以简单地 #included。但是随后你会发现 Application 的方法也需要 GameObject 也被完全定义,在 它们 之前可以编译。必须有人先走。必须首先完全定义某人。但是您有两个 class,它们的方法需要先完全定义另一个 class。

这就是为什么在 C++ 中 class 方法很少在头文件中完全定义、内联。这里,只需要在头文件中声明构造函数:

class Gameobject
{
public:
    Gameobject(std::string name);

(还不清楚 Application if 是否首先需要成为此 class 的 friend)。然后在 Application.cpp 文件中,你 #include 两个头文件,并定义构造函数:

GameObject::GameObject(std::string name)
    :name{name} // Might as well start using modern C++'s uniform initialization syntax 
    {
        Application::Get()->AddGameobject(this);
    }

由于现在两个 class 都已完全定义,通过包含它们的头文件,此构造函数现在可以编译,但前提是修复所有其他类似的相互依赖的方法。您需要从头文件中删除所有这些,并且只声明它们;然后在 .cpp 文件中完全定义它们,在 #includeing 所有必需的头文件之后,classes.