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。我该如何解决这些问题?
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。我该如何解决这些问题?
通常的方式,在你的头文件中使用#ifdef 和#define
里面game.h:
#ifndef GAME_H
#define GAME_H
.. rest of your header file here
#endif
这样,内容将被多次读取,但只定义一次。
编辑:删除了每个评论标识符开头和结尾的下划线。
关键是前向声明。从中获取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
显然,要使其工作,分离接口和实现很重要。
除了技术(前向定义和只读头)之外,您还需要弄清楚为什么您的套接字头需要游戏头中的任何内容,并将您的系统打包成具有单一依赖顺序的模块。套接字类不应该有任何理由需要知道它正在用于什么游戏。
为了完整起见,另一种选择是:
#pragma once
在文件的顶部。
这样做的好处是不会重复打开文件,节省编译时间。
它的缺点是不标准,因此并非所有编译器都支持它。在 Visual C++ 中可靠地工作。
我想不出任何优雅的方法来解决它 - 你最好的选择是前向定义实际使用的函数。因此,如果 game.h 仅使用 socket.h 中的 connect() 函数,则将这一行添加到 game.h:
void connect();
并删除 socket.h 导入。当然,如果 connect() 的签名发生变化,您还需要记住更新前向定义,因此这种解决方案远非理想。如果可能的话,修改设计以避免循环依赖。
如果 game.h 只需要知道 socket.h 中的一个类,向前定义它是这样的:
class Socket;
关于内联函数和成员对象有一些注意事项,请参阅The C++ FAQ Lite。
@lassevk,不应该是“头文件将被打开几次,但预处理器只会在第一次读取期间读取#ifndef 和#endif 之间的文件内容一次。之后预处理器将忽略bewteen PP 宏,因为 _GAME_H 已定义。”