限制包含在C++中
Limiting includes in C++
在我的新手C++项目中,我遇到了各种各样的include重载问题,但我不知道如何避免。
如何避免必须包含数十个类的问题,例如在地图加载场景中:
下面是一个简单的Map类示例,它将从以下文件加载游戏地图:
// CMap.h
#ifndef _CMAP_H_
#define _CMAP_H_
class CMap {
public:
CMap();
void OnLoad();
};
#endif
// CMap.cpp
#include "CMap.h"
CMap::CMap() {
}
void CMap::OnLoad() {
// read a big file with all the map definitions in it here
}
现在假设我有大量的怪物要加载到我的地图中,所以我可能有一个列表或其他结构来保存地图中所有的怪物定义
std::list<CMonster*> MonsterList;
然后,我可以在我的CMap.h中简单地向前声明"CMonster",并将我喜欢的任意数量的怪物添加到列表中
// CMap.h
class CMonster;
// CMap.cpp
void CMap::OnLoad() {
// read a big file with all the map definitions in it here
// ...
// read in a bunch of mobs
CMonster* monster;
MonsterList.push_back(monster);
}
但如果我有很多不同类型的怪物呢?如何在不包括每个CMonster_XXX.h的情况下创建许多不同类型的怪物?还使用这些方法?
// CMap.cpp
void CMap::OnLoad() {
// read a big file with all the map definitions in it here
// ...
// read in a bunch of mobs
CMonster_Kitten* kitty;
kitty->OnLoad();
MonsterList.push_back(kitty);
CMonster_Puppy *puppy;
puppy->OnLoad();
puppy->SetPrey(kitty);
MonsterList.push_back(puppy);
CMonster_TRex *awesome;
awesome->OnLoad();
awesome->SetPrey(puppy);
MonsterList.push_back(awesome);
}
以下是我用于包含事物的规则。
- 在头文件中尽可能多地转发声明
- 在.cpp中包含您需要的任何.h
- 除非迫不得已,否则不要将.h包含在其他.h中
- 如果您的项目构建不需要包含.h,那么您就可以了。(大多数情况下,前提是编译器足够兼容)
编辑:此外,你可能想阅读大规模C++软件设计。它谈到了管理物理文件依赖关系。
您可以创建一个类似的文件myMonstersInclude.h
#include "myMonster1.h"
#include "myMonster2.h"
....
您的主代码只需要执行`#include"myMonstersInclude.h"。
您甚至可以使用构建工具生成它,大多数工具都允许您在每一步之前和之后运行自己的脚本。
简短的回答是:你不能。
稍长的是:您可以创建一个仅#包含其他头文件的头文件,并将新的头文件包含在.cpp文件中。尽管如此,这仍然有效地包括了所有的标题,你只是没有重复的包含列表,这就是为什么我说简短的答案是你不能。
新标题类似于:
#include CMonster_cat
#include CMonster_puppy
...
问题是,你的地图真的需要了解所有类型的怪物吗?可能不会——就地图而言,只知道它们源自CMonster就足够了。地图类使用的所有方法都应该能够通过怪物上的虚拟函数进行操作,因此每个怪物类型都定义了其专门的行为,而不是地图。
我怀疑,通过正确利用这里的继承,你的"包含问题"会大大减少。
您可以使用工厂函数。与全局静态对象组合以注册类型。类似这样的东西:
// in some main file...
typedef CMonster*(*create_ptr)();
std::map<std::string, create_ptr> &get_map() {
// so we can make sure this exists...
// NOTE: we return a reference to this static object
static std::map<std::string, create_ptr> map;
return map;
}
我们添加一些粘合代码来注册创建函数。。。
// in each type of monster class (ex: CMonsterA)
CMonster *create_monster_a() {
return new CMonsterA;
}
static struct monsterA_Registrar {
monsterA_Registrar() {
get_map().insert(std::make_pair("MonsterA", create_monster_a));
}
} register_monsterA;
最后,回到主文件中,您可以根据其类型的名称创建一个怪物对象。。。
std::map<std::string, create_ptr>::iterator it = get_map().find("MonsterA");
if(it != get_map().end()) {
return (it->second)();
}
throw "invalid monster type requested";
以下是发生的情况:
当程序启动时,在main之前,它将运行全局对象的所有构造函数,在这种情况下,register_monsterA
就是其中之一。
这个对象的构造函数将获得get_map()
(它不能只是一个全局static
,因为如果我们这样做,我们无法知道初始化的顺序,所以它是一个函数)。
然后它会添加一个项目,这是一个"创建函数",基本上是一个能够制作新CMonster
的函数。
最后,要制作一个怪物,我们只需在同一张地图中查看,然后获得创建函数并运行它(如果它存在的话)。
编辑:下面是一个完整的工作示例。。。(用一些宏魔术让它更干净)
CMonster.h
class CMonster {
public:
virtual ~CMonster() {
}
virtual void roar() = 0;
};
typedef CMonster*(*create_ptr)();
std::map<std::string, create_ptr> &get_map();
#define MONSTER_REGISTRAR(name)
CMonster *create_monster_##name() {
return new C##name;
}
static struct monster##name##_Registrar {
monster##name##_Registrar() {
get_map().insert(std::make_pair(#name, create_monster_##name));
}
} register_monster##name;
CMonster.cc
std::map<std::string, create_ptr> &get_map() {
// so we can make sure this exists...
// NOTE: we return a reference to this static object
static std::map<std::string, create_ptr> map;
return map;
}
CMonsterA.cc
#include "CMonster.h"
class CMonsterA : public CMonster {
public:
CMonsterA() {
std::cout << "HERE - A" << std::endl;
}
virtual void roar() {
std::cout << "A" << std::endl;
}
};
MONSTER_REGISTRAR(MonsterA)
CMonsterB.cc
#include "CMonster.h"
class CMonsterB : public CMonster {
public:
CMonsterB() {
std::cout << "HERE - B" << std::endl;
}
virtual void roar() {
std::cout << "B" << std::endl;
}
};
MONSTER_REGISTRAR(MonsterB)
main.cc
#include "CMonster.h"
CMonster *get_monster(const std::string &name) {
std::map<std::string, create_ptr>::iterator it = get_map().find(name);
if(it != get_map().end()) {
return (it->second)();
}
throw "invalid monster type requested";
}
int main() {
CMonster *monster = get_monster("MonsterB");
monster->roar();
delete monster;
}
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从包含m行的文件中提取n行,必要时(惰性地)重复该文件
- 编译包含字符串的代码时遇到问题
- c++库的公共头文件中应该包含什么
- 将包含C样式数组的对象初始化为成员变量(C++)
- 是否需要删除包含对象的"pair"?
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 使用mongocxx驱动程序时包含头文件问题
- 如何在h文件中包含.o对象文件
- 在混合代码库中将C转换为C++时出现许多包含错误
- VS2017,C++包含目录与附加包含目录,子文件夹包含失败-但为什么
- cmath抛出错误C2062、C2059、C2143和C2447.cmath包含在矢量文件中
- 为什么您需要C++头文件的包含保护
- 无法在UE4中包含BP类到CPP类
- g++ 说函数不存在,即使包含正确的标头
- 在C++代码中包含opencv时,使用ctypes创建.so文件
- Visual C++GC接口如何启用它以及要包含哪个库
- 当调用switch语句中的函数时(即使函数不包含循环),似乎是永不结束的循环的问题
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)