【问题标题】:declare obj-c class interface that contain c++ class type ivar声明包含 c++ 类类型 ivar 的 obj-c 类接口
【发布时间】:2011-11-18 10:34:48
【问题描述】:

目前我正在做一个 cocos2d+Box2D 项目,所以我处理了一些 Objective-C++ 代码。

而我正面临这样的情况:

#import "cocos2d.h"
#import "Box2D.h"

@interface BasicNode : CCNode {
@private
    ccColor3B _color;
    b2Body *_body;
    b2Fixture *_shape;
}

b2Body 和 b2Fixture 是在 Box2D.h 中定义的 C++ 类

如果BasicNode的实现命名为BasicNode.mm就可以了。

但是如果我有另一个名为 Game.m 的文件使用 BasicNode 并导入 BasicNode.h,它将无法编译,因为 .m 文件是 Obj-C 文件并且不了解 C++ 代码。

所以我决定将#import "Box2D.h" 移到实现文件中,并且只在头文件中保留类型声明(这正是头文件应该包含的内容)。

但是我该怎么做呢?它们是 C++ 类类型,但它们实际上只是一个指针,所以我写了一些辅助宏

#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) struct clsname; typedef struct clsname clsname
#endif

CLS_DEF(b2Body);
CLS_DEF(b2Fixture);

只有当 CLS_DEF(b2Body) 只出现一次时它才有效。否则编译器会找到同名的多个类型声明,即使它们是相同的。比我必须更改为

#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) @class clsname
#endif

它现在正在工作。

但我认为将 C++ 类类型声明为 Obj-C 类并不是一个好主意,尤其是我使用 ARC。

有没有更好的方法来处理它?而且我真的不想做这样的事情

@interface BasicNode : CCNode {
@private
    ccColor3B _color;
#ifdef __cplusplus
    b2Body *_body;
    b2Fixture *_shape;
#else
    void *_body;
    void *_shape;
#endif
}

编辑:另外请告诉我我的调整方式会引入任何问题吗?通过使 C++ 类 ivar 看起来像其他纯 Obj-C 代码的 Obj-C 类。

【问题讨论】:

    标签: objective-c cocos2d-iphone objective-c++ box2d-iphone


    【解决方案1】:

    一个简单的解决方案是将Game.m 重命名为Game.mm

    【讨论】:

    • 是的..它会做的。我只需要记住将每个 .m 文件改为 .mm 文件。
    • 没错,一旦您使用 C++ 标头,它可能会“传播”到其他类,因此从那时起,最好将 .mm 用作所有 ObjC 类的默认扩展名。
    • 请注意,“无处不在”包含 C++ 头文件可能会降低编译速度。取决于项目的大小、头文件的复杂性(STL、boost 和类似的最差)以及您的计算机速度。
    • @LearnCocos2D:如果你正确使用标题,它没有理由需要“传播”。标头一般不必导入其他标头。头文件需要导入另一个头文件的唯一情况是当您子类化另一个头文件中声明的类,或实现另一个头文件中声明的协议,或使用另一个头文件中定义的 typedef 或枚举或结构时。这些不是常见的情况。大多数时候,标头不会导入其他标头。并且只有使用 C++ 头文件的实现文件才需要成为 C++。
    • 正确。我的意思是,如果有人不知道您所说的内容以及如何使用前向声明,那么很容易到处导入标头并一遍又一遍地遇到相同的问题。
    【解决方案2】:

    有几种方法。如果您可以依赖使用 Objective-C 2.2 运行时的功能,您可以在类(类别)扩展中添加 ivars。这意味着您可以在类的 .mm 文件中添加 ivars,并保持 .h 文件中没有任何 C++ 内容。

    如果您需要支持旧版本的运行时,有几种方法比#ifdefing 更好。在我看来,最好的方法是使用 C++ 中常见的 'pimpl' 习语——你在头文件中声明一个实现结构,并添加一个指向这种结构的指针的 ivar。在您的类的实现 (.mm) 中,您实际上定义了该结构及其所有 C++ 成员。然后,您只需在 init... 方法中分配该实现对象,并在 newdelete 中分配 dealloc

    我已经编写了 pimpl 习语,因为它适用于在 this article 中干净地混合 Objective-C 和 C++ - 它还显示了您可以考虑的一些其他潜在解决方案。

    【讨论】:

    • 看起来不错,除了 C++ 类型成员没有更多的综合属性。如果它不会引入新问题,我仍然更喜欢我的调整方式。
    • 您仍然可以合成 PIMPL 类型的属性。将指向 C++ 对象的指针视为指向 Obj-C 类的指针似乎是个坏主意,但只要不使用 ARC 就可以了。
    【解决方案3】:

    使用 Xcode 5,您不必在头文件中声明实例变量,只需在实现文件中声明它们即可。因此,您的 BasicNode 头文件不会被 C++“污染”。

    您可以在 C++ 中使用“struct”而不是“class”。唯一的区别是,在一个类中,所有成员默认都是私有的,而在结构中,它们默认是公共的。但是你可以用一个结构来做任何你可以用一个类来做的事情。这样你就可以写例如

    struct b2Body;
    struct b2Fixture; 
    

    在您的界面之外,并且

    { ...
        struct b2Body* _body;
      ...
    }
    

    在您的界面中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多