还是OC好用,请问C++怎么实现类似的效果:isKindOfClass来查询是否属于某个类。
当然,子类也应该认为是一种基类。也就是说子类调用isKindOfClass(基类)也是成立的。
用dynamic_cast(var)进行转换,type是你的转换后的类型,var是你要转换的对象。以下是使用注意事项:
- 二者必须同时是指针或引用类型
- 当转换指针类型时,若转换成功,则得到type类型对象;若转换失败,返回nullptr。当转换引用类型时,若转换成功,则得到type类型对象;若转换失败,则抛出异常。
- dynamic_cast通常用于向下转型,即向子类转型(语言已经支持直接向上转型),必须注意,当你在做向下转型时,当前对象类型中必须含有虚函数,否则编译报错。
- 支持交叉转换,即当var类型并非继承于X,但实际类型继承于X,则dyanmic_cast(var)同样成功
- dynamic_cast开销相当大,尽量少用
因为语言关系,C++对RTTI(运行时类型识别)支持相当有限,因此尽量使用多态来达到目的,而非判断类型。
楼主的问题,可以封装一个isKindOf()的宏,如:
#define isKindOf(type, instance) (dynamic_cast(instance) != nullptr)
但强烈不推荐这样做,如果能够确定类型,推荐使用static_cast进行转换,开销非常小
其实我这个问题是来自Box2d里面b2Body对象的getUserData()方法,它返回的正好是个void*。
然而,我的代码中会有继承关系。比如,BigBoss、SmallBoss都派生自Boss基类。然后都会将它们setUserData(xxx)到b2Body上。
但是,getUserData()取到的指针的时候,就很难处理了,dynamic_cast没办法cast void*。
C++ 反射比较薄弱,不过可以通过dynamic_cast来判断
— Begin quote from ____
引用第2楼maxxfire于2016-01-12 12:36发表的 回 1楼(prompthu) 的帖子 :
其实我这个问题是来自Box2d里面b2Body对象的getUserData()方法,它返回的正好是个void*。
然而,我的代码中会有继承关系。比如,BigBoss、SmallBoss都派生自Boss基类。然后都会将它们setUserData(xxx)到b2Body上。
但是,getUserData()取到的指针的时候,就很难处理了,dynamic_cast没办法cast void*。 http://www.cocoachina.com/bbs/job.php?action=topost&tid=456050&pid=1478716
— End quote
如果是能接受足够多的编辑的,可以参考下现在2dx中读取studio导出文件生成UIWidget部分的代码。
楼主,你的问题应该属于框架问题,box2d是一个独立的框架,因此无法得知userdata的具体类型。要让box2d配合你的游戏,你必须自己实现box2d到你的游戏逻辑的衔接,一个比较普遍和简单的方法就是创建一个基类型,或者直接继承于Node或子类(不建议这样做,因为Node是用来渲染的,而不是逻辑),该基类型应当类似于java的接口,并在子类中实现,基类中设置getKind这样的抽象方法,这样在你的逻辑(主要是碰撞回调)就可以取得该基类型的对象,调用getKind辨识出具体的碰撞类型,但是这种方法有一个特点,必须始终记住只要派生出一个子类,就必须实现getKind方法,否则就会调用父类的getKind方法。
此外,有一点要牢记,在碰撞回调中得到的对象是抽象类型,通过getKind,您已经可以确定他的具体类型,这时,如果您需要类型转换得到子类型对象,请务必使用dynamic_cast,另外在设置userdata的时候使用static_cast转换到抽象基类再进行设置;还有一种更快速的方法,即用大括号强制转换过去(无任何CPU开销),不过这种方法是原始的C语言类型转换,不包含任何类型内部处理(主要是虚函数表的处理),所以当采用此方法时,请确保您的抽象基类要作为子类的第一继承位,setuserdata时,可以直接设置,而无需static_cast转换。当然,在C++中我们也不鼓励第二种方法,当出现其他需求要求我们的第一继承位必须是某个类的时候,这种方法就不能用了,比如使用成员函数指针时的类继承关系设计就需要特别谨慎
不错,用统一基类就可以避免dynamic_cast void*指针的问题,那就可以使用dynamic_cast来解决这个问题了。
不过你说的getKind又是一种什么方式呢,getKind应该返回什么呢,不大明白如何使用?
这么说吧,你从userdata得到基类对象,但由于所有碰撞对象都是统一基类的,你仍然不知道具体是什么类型,从而转换过去,所以在该基类中设计getKind这样的方法,就可以知道具体类型,从而使用dynamic_cast转换过去,例如:
class Collider { public: virtual ~Collider() {} virtual string getKind() const = 0; }; class Fruit : public Collider { private: b2Body *m_body; public: string getKind() const override { return "fruit"; } void createBody(b2World &world) { ... // 创建body m_body->SetUserData(static_cast(this)); // 如果Collider不是第一继承位,则static_cast是必要的,这里可以省略 } }; class Monkey : public Collider { private: b2Body *m_body; public: string getKind() const override { return "monkey"; } void createBody(b2World &world) { ... // 创建body m_body->SetUserData(static_cast(this)); // 如果Collider不是第一继承位,则static_cast是必要的,这里可以省略 } void eat(Fruit *fruit) { ... } }; void MyContactListener::BeginContact(b2Contact* contact) { auto colliderA = (Collider*)contact->GetFixtureA()->GetBody()->GetUserData(); auto colliderB = (Collider*)contact->GetFixtureB()->GetBody()->GetUserData(); Monkey *monkey = nullptr; Fruit *fruit = nullptr; if(colliderA->getKind() == "monkey" && colliderB->getKind() == "fruit") { monkey = dynamic_cast(colliderA); fruit = dynamic_cast(colliderB); } else if(colliderB->getKind() == "monkey" && colliderA->getKind() == "fruit") { monkey = dynamic_cast(colliderB); fruit = dynamic_cast(colliderA); } if(monkey != nullptr && fruit != nullptr) { monkey->eat(fruit); game->delayDestroy(fruit); // 不可以在回调中删除body,所以必须延迟删除 } ... } ``` 通常,在碰撞回调中写逻辑会使得该回调非常臃肿,所以,在Collider类中可以设计onCollideBegin和onCollideEnd虚函数,则Monkey和Fruit类的onCollideBegin可能长这个样子:void Monkey::onCollideBegin(Collider *collider, Contact *contact) { if(collider->getKind() == "fruit") { eat(dynamic_cast(collider)); // 这里eat方法可以设为private访问,加强封装 } else if(collider->getKind() == "coin") { ... } ... } void Fruit::onCollideBegin(Collider *collider, Contact *contact) { if(collider->getKind() == "monkey") { game->delayDestroy(this); // 不可以在回调中删除body,所以必须延迟删除 } else if(...) { ... } ... } // ContactListener中的BeginContact回调可能是这样 void MyContactListener::BeginContact(b2Contact* contact) { auto colliderA = (Collider*)contact->GetFixtureA()->GetBody()->GetUserData(); auto colliderB = (Collider*)contact->GetFixtureB()->GetBody()->GetUserData(); colliderA->onCollideBegin(colliderB, contact); colliderB->onCollideBegin(colliderA, contact); } ```
明白了,getKind就是相当于OC中的isKindOf,感谢感谢~