0

我正在使用一个名为 Bullet 的物理库,但在阻止将 Bullet 标头包含到我的物理标头中时遇到了麻烦。

//Physics.h
#include <BulletPhysics/btBulletDynamicsCommon.h> // Contains lots of other headers

struct Physics
{
    static btDiscreteDynamicsWorld* pDynamicsWorld;
    static btAlignedObjectArray<btCollisionShape*> collisionShapes;
    static vec3 findSomePoint();
};

现在在我的代码的其他各个部分中,我可能想要访问 Physics 结构,为此我需要包含 Physics 头文件,但这也将包括任何其他 CPP 文件中的所有库头文件。

我试图想办法让库头文件只包含在 Physics.cpp 中,但是如果我从 Physics.h 中删除头文件,我无法让编译器识别结构定义中的库类型。

对于一些成员,我可以只转发声明并且工作正常,但它不适用于非指针或引用类型,因为需要完整的定义。

我注意到,如果我使用命名空间,我可以将成员声明为 extern,并且不需要完整的类型定义。但它会导致与前向声明奇怪的事情:

struct btDiscreteDynamicsWorld; // This seems fine
template <typename T>
struct btAlignedObjectArray; // This works but seems really hacky and I'm not sure if it's even valid

namespace Physics
{
    extern btDiscreteDynamicsWorld* pDynamicsWorld;
    extern btAlignedObjectArray<btCollisionShape*> collisionShapes; // Complete type not needed
    vec3 findSomePoint();
}

同样使用命名空间我失去了使用访问说明符的能力。

我还认为,我可以使用 getter 函数和返回引用,而不是让成员自己在结构中,但返回类型也至少需要一个前向声明(我认为不是完整类型),我仍然必须前向声明该模板键入我之前所做的方式。

最好的方法是什么?或者我是否过于强调防止复制额外的标题。一个标题包含许多其他标题,所以很多,所以我不确定我是否应该关心。

4

1 回答 1

0

这两个结构具有相似的含义,除了第二个允许您选择创建和初始化内存的位置。我个人不会太担心你项目的递归阅读器包含的线索很大。您实际上在这里使用全局变量来处理一些非常重要的共享状态,这通常表明您的设计中有一个流程。如果您的程序是多线程的并且无法绕过共享状态,

我建议使用第三种方法并在 getter 和 setter 中执行一些互斥(readers-waiter lock 似乎合适)在这种情况下,您需要以某种方式确保仅通过 setter 修改状态(可能通过从吸气剂。

如果您要坚持以extern非平凡的方式解决和初始化这些对象(我认为不是),您还想定义某种初始化函数并确保在其他任何事情之前调用它,在这种情况下您应该考虑改写一个 Singleton 类,这就是我会做的。

于 2018-01-05T10:17:22.480 回答