我在做什么: 我有两个文件:game.h和sound_controller.h。他们俩都互相冲突。 game.h需要一个SoundController,而sound_controller.h需要一个Game,所以他们都要包含彼此的头文件。
问题: game.h第26行:
error: field soundController has incomplete type 'SoundController'
我在game.h中包含了sound_controller.h,但是它说类型不完整但我已经声明了SoundController类。那么我该如何解决这个问题?
代码:
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使用Game,因为它调用了Game.h的静态函数:log。
修改
删除&#34; #include game.h&#34;来自sound_controller.h。现在在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:
解决方案是将#include&#34; game.h&#34;在sound_controller.cpp。
答案 0 :(得分:2)
你有一个循环依赖 - 你的每个头文件都试图#include另一个;但是你不能两种方式:首先解析两个标题中的一个,当解析器解析第一个标题时,它还不知道(还)第二个标题的内容(这是为什么它抱怨&#34;不完整的类型&#34;)。
在这种情况下,简单的解决方法是删除&#34; #include Game.h&#34;来自sound_controller.h文件的行。
答案 1 :(得分:0)
对我来说,你似乎仍然迷失了。让我们简化一下测试用例:
class A {
public:
A();
B b;
};
class B {
public:
B();
A a;
};
A::A() {;}
B::B() {;}
当编译器计算出A时,编译器必须知道其字段b的类型B.A&#b; b是一个成员数据,其内存将被分配为A被实例化。要知道它,编译器需要这个:
class B {
public:
B();
A a;
};
同样,为了让编译器弄清楚应该如何为B的a分配内存,编译器应该知道A.因此,它是一个循环依赖,并且无法解决。
幸运的是,你的SoundController没有Game,而Game有SoundController。因此,没有循环依赖。只有那个类SoundController {...}应该出现在Game {...}类之前。
但是,如果在我的例子中,A和B相互依赖或者多个类之间存在循环依赖关系,那么这将是一个问题。
要解决这个问题,如果字段的类型是类,则前向声明本身并不起作用。换句话说,如果在实例化类本身时应该为字段分配一些内存并进行布局,则前向声明没有帮助。仅当字段的类型是指向其他类的指针或引用时,它才有用。在我的例子中,前向声明仅在A的场B为B *或B&amp;时起作用。
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
};
现在,编译器知道它将为A分配多少内存以及内存布局是什么。在那,编译器不必知道B的大小左右。它只需要知道指针的大小。在解决了A的内存布局后,编译器可以解决以下B内存布局等问题。