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

时间:2017-07-11 08:17:27

标签: c++ inheritance include composition circular-dependency

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

以下是我的相关课程:

Component.h

#pragma once
#include "Vector3.h"

class GameObject;

class Component {
public:
    GameObject* gameObject = nullptr;

    Component();
};

Component.cpp

#include "Component.h"

Component::Component() {}

Updateable.h

#pragma once

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

class GameObject;

class Updateable : public Component {

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

Updateable.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;
};

GameObject.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&#39;可更新&#39 ;:基类未定义基本平台c:\ users \ default.sixcore-pc \ documents \ visual studio 2015 \ projects \ basic platformer \ basic platformer \ playercontroller.h 9

2 个答案:

答案 0 :(得分:2)

您的许多文件都有#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不需要任何#include个。它需要class GameObject;的声明。

答案 1 :(得分:0)

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