【发布时间】:2013-04-18 17:03:30
【问题描述】:
在基类的指针上调用成员函数(指针)是否“安全”(和/或可移植),但指向的对象是不同派生类的实例。成员函数不访问派生类的任何成员变量或函数。
/* Shortened example of what happens in the client code and library */
class Base { /* ... */ }
class DerivedA : public Base {
/* ... */
public: void doSomethingA(float dt);
}
void DerivedA::doSomethingA(float dt) {
/* Does not access members. Conventionally calls/accesses statics */
cout << "dt(" << dt << ")";
}
class DerivedB : public Base { /* ... */ }
typedef void (Base::*SEL_SCHEDULE)(float);
SEL_SCHEDULE pCallback = (SEL_SCHEDULE)(&DerivedA::doSomethingA);
DerivedB db = new DerivedB();
Base *b = &db;
/* pCallback and b are saved in a list elsewhere (a scheduler) which calls */
(b->*pCallback)(0.f);
这似乎工作(在 MSVC/调试模式下)在运行时没问题,但我想知道这是否是 Bad (TM) - 为什么? (我还没有用 Android 和 iOS 的编译器测试这段代码)。
如果需要,请提供更多细节:我正在构建一个基于 cocos2d-x 的项目。 Base 是 CCObject、DerivedA 和 DerivedB 是 CCLayer 的子类。
层次结构是DerivedA 和DerivedB CCLayer CCNode CCObject。它们是游戏场景,它们在相互排斥的时间可见/活跃。
DerivedA 有一个不同的静态函数来设置音乐的播放,它接收一个 CCNode 调用者对象作为参数,schedules 另一个 selector (doSomethingA) 到 开始播放并慢慢淡入使用类似的东西:
callerNode->schedule(schedule_selector(DerivedA::doSomethingA), 0.05f);
schedule_selector 是 C 风格的演员。 doSomethingA 不访问其任何成员变量或调用成员函数。它访问静态成员并调用其他静态函数,例如such as
CocosDenshion::SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(sFadeMusicVolume);
在运行时对doSomethingA 的调用发生在CCTimer::update。
hack 主要是为了避免重复代码并符合库的回调签名(定时器/调度器系统)。
【问题讨论】:
-
您需要
reinterpret_cast这一事实应该会敲响警钟。不幸的是,你隐藏了邪恶的 C 风格演员背后的危险。 -
回调函数不使用任何类成员信息并简单地打印其参数这一事实隐藏了可能存在的错误。这当然不是正确的代码。回调的 C 风格类型转换破坏了类型安全。