Incomplete type, but I'm using forward declarations?

前提是你 提交于 2019-12-24 06:43:14

问题


What I'm doing: I have two files: game.h and sound_controller.h. Both of them are having conflicts with each other. game.h requires having a SoundController, while sound_controller.h requires having a Game, so they're both including each other's header files.

The problem: game.h line 26:

error: field soundController has incomplete type 'SoundController'

I've included sound_controller.h in game.h, but it's saying the type is incomplete yet I've declared the class SoundController. So how do I fix this?

The code:

game.h:

#pragma once

/**
    game.h
    Handles highest level game logic
*/

#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <SDL_mixer.h>

#include <iostream>

#include "config.h"
#include "sound_controller.h"
#include "image_controller.h"

#include <stdarg.h>

class SoundController; //forward declaration

class Game {

    ImageController imageController;
    SoundController soundController;

    SDL_Window* window = NULL;

    bool running;


public:
    Game();
    bool init();
    bool createWindow();

    void update();

    static void log(const char* format, ...);

};

sound_controller.h:

#pragma once

/**
    sound_controller.h
    Allows for manipulation with sound (sound effects and music)
*/

#include "config.h"
#include <SDL_mixer.h>
#include "Game.h"

class Game; //forward declaration

class SoundController {

    bool init();
    bool load_sound();
    bool load_music();

    void music_play();
    void music_pause();
    void music_stop();

};

sound_controller.cpp uses Game because it calls Game.h's static function: log.

EDIT:

Removed "#include game.h" from sound_controller.h. Now getting another error this time in sound_controller.cpp:

line 8 error: incomplete type 'Game' used in nested name specifier

sound_controller.cpp:

#include "sound_controller.h"

bool SoundController::init() {
    bool success = true;

    //Initialize SDL_mixer
    if( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 2048 ) < 0 ) {
        Game::log( "SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError() );
        success = false;
    }

    return success;

}

EDIT2:

Solution was to put #include "game.h" in sound_controller.cpp.


回答1:


You've got a circular dependency -- each of your header files is trying to #include the other; but you can't have it both ways: one of the two headers will be parsed first, and when the parser parses the first header it won't know (yet) about the contents of the second one (which is why it complains about "incomplete type").

The easy fix in this case would be to remove the "#include Game.h" line from your sound_controller.h file.




回答2:


To me, you seem to be still lost. Let's simplify the test case:

class A {
public:
    A();
    B b;
};

class B {
public:
    B();
    A a;
};

A::A() {;}
B::B() {;}

When the compiler figures out about A, the compiler must know about its field b's type B. A's b is a member data whose memory is going to be allocated as A is instantiated. To know it, compiler needs this:

class B {
public:
    B();
    A a;
};

Likewise, in order for the compiler to figure out how the memory should be allocated for B's a, the compiler should know about A. Thus, it is a circular dependency, and can't be solved.

Fortunately, your SoundController does not have Game while Game has SoundController. Thus, there's no circular dependency. Only that class SoundController { ... } should come before class Game { ... }.

However, if, in my example, A and B depends each other or there are a circular dependency among multiple classes, it would be a problem.

To solve it, forward declaration itself doesn't help if the field's type is a class. In other words, if some memory should be allocated and layout'ed for the field when the class itself is instantiated, the forward declaration doesn't help. It helps only when the field's type is a pointer or reference to other class. In my example, the forward declaration works only when A's field b is B* or B&.

class A {
public:
    A();
    B* b; // whatever the pointer points to, the size of the pointer is given
};

class B {
public:
    B();
    A a; // the compiler now can fine A from above
};

Now, the compiler knows how much memory it will allocate for A and what's the memory layout. In that, the compiler does not have to know about B's size or so. It just needs to know the size of a pointer. As A's memory layout is solved, the compiler can solve the following B's memory layout, etc.



来源:https://stackoverflow.com/questions/46616515/incomplete-type-but-im-using-forward-declarations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!