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