5
game.h needs:
- packet.h
- socket.h

server.h needs:
- socket.h

socket.h needs:
- game.h

当我尝试将 socket.h 包含到 game.h 中时,问题就出现了,因为 socket.h 已经包含了 game.h。我该如何解决这些问题?

4

6 回答 6

17

通常的方式,在你的头文件中使用#ifdef 和#define

里面game.h:

#ifndef GAME_H
#define GAME_H

.. rest of your header file here

#endif

这样,内容将被多次读取,但只定义一次。

编辑:删除了每个评论标识符开头和结尾的下划线。

于 2008-12-21T13:02:38.120 回答
9

关键是前向声明。从中获取game.h所需的内容socket.h(反之亦然),然后在另一个标题中前向声明它,例如game_forwards.h. 例如,考虑以下情况:

// game_fwd.h

#ifndef GAME_FWD_H
#define GAME_FWD_H

class game;

#endif // ndef GAME_FWD_H

// game.h

#ifndef GAME_H
#define GAME_H

#include "socket.h"

class game {
    socket* m_sck;
};

#endif // ndef GAME_H

// socket.h

#ifndef SOCKET_H
#define SOCKET_H

#include "game_fwd.h"

class socket {
    game* m_game;
};

#endif // ndef SOCKET_H

显然,要使其工作,分离接口和实现很重要。

于 2008-12-21T13:05:31.483 回答
8

除了技术(前向定义和只读头)之外,您还需要弄清楚为什么您的套接字头需要游戏头中的任何内容,并将您的系统打包成具有单一依赖顺序的模块。套接字类不应该有任何理由需要知道它正在用于什么游戏。

于 2008-12-21T15:50:28.107 回答
3

为了完整起见,另一种选择是:

#pragma once 

在文件的顶部。

这样做的好处是不会重复打开文件,节省编译时间。

它的缺点是不标准,因此并非所有编译器都支持它。在 Visual C++ 中可靠地工作。

于 2008-12-21T16:02:07.243 回答
1

我想不出任何优雅的方法来解决它 - 你最好的选择是前向定义实际使用的函数。因此,如果 game.h 仅使用 socket.h 中的 connect() 函数,则将这一行添加到 game.h:

void connect();

并删除 socket.h 导入。当然,如果 connect() 的签名发生变化,您还需要记住更新前向定义,因此这种解决方案远非理想。如果可能的话,修改设计以避免循环依赖。

如果 game.h 只需要知道 socket.h 中的一个类,向前定义它是这样的:

class Socket;

关于内联函数和成员对象有一些注意事项,请参阅The C++ FAQ Lite

于 2008-12-21T13:17:57.480 回答
0

@lassevk,不应该是“头文件将被打开几次,但预处理器只会在第一次读取期间读取#ifndef 和#endif 之间的文件内容一次。之后预处理器将忽略bewteen PP 宏,因为 _GAME_H 已定义。”

于 2008-12-21T13:08:24.253 回答