C ++头文件 - 困惑!

时间:2008-12-21 13:01:08

标签: c++ header-files

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。我该如何解决这些问题?

6 个答案:

答案 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。“