带有前向声明的循环包含和继承导致 C2504 基类未定义

Posted

技术标签:

【中文标题】带有前向声明的循环包含和继承导致 C2504 基类未定义【英文标题】:Circular Inclusion and Inheritence with Forward Declarations Leads to C2504 base class undefined 【发布时间】:2017-07-11 08:17:26 【问题描述】:

我在 PlayerController.h 中收到 C2504 编译错误,指出我的基类(可更新)未定义。我已经搜索了几个小时来寻找具有继承问题的循环包含的解决方案,他们的解决方案是删除循环包含并使用前向声明。据我了解,如果没有调用前向声明类中的方法,则此方法有效。但是,在我的程序中,我的 Updateables 类调用其成员继承的 gameObject 对象的方法,而 GameObjects 也调用其成员 Updateables 的方法。因此,Updateables 需要包含 GameObject.h,而 GameObjects 需要包含 Updateables.h。这导致 PlayerController.h 中的 C2504 表示找不到基类 Updateable。

这是我的相关课程:

组件.h

#pragma once
#include "Vector3.h"

class GameObject;

class Component 
public:
    GameObject* gameObject = nullptr;

    Component();
;

组件.cpp

#include "Component.h"

Component::Component() 

可更新.h

#pragma once

#include "Component.h"
#include "GameObject.h"

class GameObject;

class Updateable : public Component 

public:
    ~Updateable();
    virtual void update() = 0;
;

可更新的.cpp

#include "Updateable.h"

Updateable::~Updateable()  

    if (gameObject) 
        gameObject->removeUpdateable(this);
    

GameObject.h

#pragma once

#include "Updateable.h"
#include "GameManager.h"

class Updateable;

class GameObject 

public:

    GameObject();
    ~GameObject();

    void runUpdateables();
    void addUpdateable(Updateable* updateable);
    void removeUpdateable(Updateable* updateable);

private:
     vector<Updateable*> updateables;
;

游戏对象.cpp

#include "GameObject.h"

GameObject::GameObject() 

    updateables = vector<Updateable*>();
    GameManager::addGameObject(this);


GameObject::~GameObject() 

    GameManager::removeGameObject(this);


void GameObject::runUpdateables() 

    for (unsigned int i = 0; i < updateables.size(); i++) 
        updateables[i]->update();
    


void GameObject::addUpdateable(Updateable* updateable) 

    updateables.push_back(updateable);
    updateable->gameObject = this;


void GameObject::removeUpdateable(Updateable* updateable) 

    auto it = find(updateables.begin(), updateables.end(), updateable);
    if (it != updateables.end()) 
        updateables.erase(it);
    

PlayerController.h

#pragma once

#include "Updateable.h"
//#include "GameObject.h"
#include "Input.h"

class Updateable;

class PlayerController : public Updateable 

public:

    float speed = 5.0f;

    void update();
;

PlayerController.cpp

#include "PlayerController.h"

void PlayerController::update() 

    float x = 0;

    if (Input::getKeyDown(GLFW_KEY_A)) 
        x = -speed;
    

    if (Input::getKeyDown(GLFW_KEY_D)) 
        x = speed;
    

    cout << x << endl;

    gameObject->getRigidBody()->velocity.x = x;
    //yes this is a method in GameObject that I removed from this post
    //because it would take up more space, rigidbody.h does not create
    //a circular dependency

GameManager.h

#pragma once

#include "GameObject.h"
#include "PlayerController.h"

class GameManager 

public:

    static void init();
    static void addGameObject(GameObject* go);
    static void removeGameObject(GameObject* go);

    static void onFrame();

private:
    static vector<GameObject*> gameObjects;
    static GameObject* box;

GameManager.cpp

#include "GameManager.h"

vector<GameObject*> GameManager::gameObjects;
GameObject* GameManager::box;

void GameManager::init() 

    gameObjects = vector<GameObject*>();

    box = new GameObject();
    box->addUpdateable(new PlayerController());


void GameManager::addGameObject(GameObject* go) 
    gameObjects.push_back(go);


void GameManager::removeGameObject(GameObject* go) 

    auto it = find(gameObjects.begin(), gameObjects.end(), go);
    if (it != gameObjects.end()) 
        gameObjects.erase(it);
    


void GameManager::onFrame() 

    for (unsigned int i = 0; i < gameObjects.size(); i++) 
        gameObjects[i]->runUpdateables();
    

这是确切的错误消息:错误 C2504 'Updateable': base class undefined Basic Platformer c:\users\default.sixcore-pc\documents\visual studio 2015\projects\basic platformer\basic platformer\playercontroller.h 9

【问题讨论】:

为什么在Updateable中转发声明PlayerController 你确实使用了Component,你在Updatable中使用它,所以你需要定义它的构造函数。 您需要在使用对象的文件中包含必要的头文件。在具有正确前向声明的头文件中,您根本不需要包含任何头文件。 @RickAstley 我在尝试寻找解决方案时不小心把它留在了那里,我没想到它会起作用,它没有 @Galik 我添加了我的component.cpp,它有一个构造函数,但里面什么都没有,所以我最初没有添加它 【参考方案1】:

您的许多文件都有#include "Class.h"class Class; 声明。你永远不需要两者;使用其中一个。

X 类的定义必须在以下情况下可见:

访问X的成员 创建X 类型的对象 定义一个派生自X的类 使用X 作为模板的模板参数,该模板要求相应的模板参数是一个完整的类型(例如标准库容器对其元素类型的要求)。请注意,这适用于使用X,而不是X*

在其他情况下(例如创建指向X 的指针或声明返回X 的函数),非定义声明(class X;)就足够了。

使用这些规则(加上必要时将函数体从头文件移动到源文件),您可以解决任何循环依赖问题。


直接处理您的文件:

Updateable.h 不需要#include "GameObject.h"。它甚至不需要GameObject 的前向声明。 GameObject.h 不需要两个 #include 中的任何一个。 GameManager.h 不需要任何 #includes。不过,它需要声明 class GameObject;

【讨论】:

【参考方案2】:

后代类必须知道基类的完整定义。前向声明是不够的,也是没用的。

【讨论】:

只有在类被实例化或访问成员时才需要完整的定义。只是为了继承,所有需要的是声明的符号。 @Someprogrammerdude 恐怕这不是真的。定义派生类需要定义它的所有基类;否则它无法确定布局和大小。 @Angew 如果在类被实例化时完整定义可用(例如通过包含所有需要的头文件),那么在头文件中可以使用class A; class B : public A ... ; 而无需显式包含头文件类A的文件。 @Someprogrammerdude 这只是not true。 N4659,[class.derived] 13/2:“一个 class-or-decltype 应表示一个类类型不是一个不完全定义的类(第 12 条)。 base-specifierclass-or-decltype 表示的类称为正在定义的类的直接基类。” (强调我的) 如果您在后代的标题之前包含基类的标题,它可能会起作用。可能是因为幸运地订购了标题。

以上是关于带有前向声明的循环包含和继承导致 C2504 基类未定义的主要内容,如果未能解决你的问题,请参考以下文章

error C2504: 未定义基类

Qt - moc 导致 C2504:基类未定义

错误 C2504:'BASECLASS':基类未定义

我需要相互包含两个头文件,而不是使用前向声明导致“不完整类型”错误

错误 C2504 - 未定义基类

包含命名空间的类模板的转发声明会导致编译错误