C ++:对不同派生类对象的基指针调用(派生的)成员函数 [英] C++: call (derived's) member function on base pointer of a different derived class's object

查看:238
本文介绍了C ++:对不同派生类对象的基指针调用(派生的)成员函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是安全(和/或可移植)调用基类指针上的成员函数(指针),但指向的对象是实例不同的派生类。成员函数不访问派生类的任何成员变量或函数。

Is it "safe" (and/or portable) to call a member function (pointer) on the pointer of a base class, but the object pointed to is an instance different derived class. The member function does not access any member variables or functions of the derived class.

/* 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 / Debug模式) ,但我想知道这是否是Bad(TM) - 为什么? (我还没有用Android和iOS的编译器测试这个代码)。

This seems to work (in MSVC/Debug mode) okay at runtime, but I'm wondering whether this is Bad (TM) - and why? (I'm yet to test this code with the compilers for Android and iOS).

一些更多的细节,如果需要:我建立一个 cocos2d-x 项目。 Base CCObject DerivedA DerivedB CCLayer

Some more specifics if required: I'm building a cocos2d-x based project. Base is CCObject, DerivedA and DerivedB are subclasses of CCLayer.

层次结构是 DerivedA DerivedB < CCLayer < CCNode < CCObject

The hierarchy is DerivedA and DerivedB < CCLayer < CCNode < CCObject. They're game scenes which are visible/alive at mutually exclusive times.

DerivedA 有不同的设置音乐播放的静态功能,它接收 CCNode 调用者对象作为参数和 schedule 另一个 selector doSomethingA )到开始播放, / em>使用类似:

DerivedA has a different static function to set up playback of music which receives a CCNode caller object as a parameter and schedules another selector (doSomethingA) to begin playback and slowly fade it in using something like:

callerNode->schedule(schedule_selector(DerivedA::doSomethingA), 0.05f);

schedule_selector 是C风格的。 doSomethingA 不访问其任何成员变量或调用成员函数。它访问静态成员,并调用其他静态函数,例如例如

schedule_selector is what does the C-style cast. doSomethingA does not access any of its member variables or call member functions. It accesses static members and calls other static functions such as such as

CocosDenshion::SimpleAudioEngine::sharedEngine()->setBackgroundMusicVolume(sFadeMusicVolume);

在运行时调用 doSomethingA CCTimer :: update

主要是为了避免重复的代码,并符合库的回调签名(timer / scheduler system)。

The hack is primarily to avoid duplicating code and conform to the library's callback signature (timer/scheduler system).

推荐答案

这是UB。

你甚至可以使用static_cast而不是可恶的C风格的cast,本身是相当合法的。但

You can even use static_cast instead of the odious C-style cast, and the cast itself is quite legal. But


[注意:尽管类B不需要包含原始成员,
动态类型的对象到成员是
dereferenced必须包含原始成员;见5.5。 -end note](5.2.9 12)

[Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. —end note ] (5.2.9 12)

第一个操作数称为对象表达式,如果对象表达式的动态
类型不包含
指针指向的成员,行为未定义(5.5 4)

"The first operand is called the object expression. If the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined" (5.5 4)

你可以从一个动态类型DerivedB的对象调用它。

I.e., you go undefined when you call it from an object of dynamic type DerivedB.

现在,随着脏的hacks走,它可能不是最糟糕的(比手动遍历vtables更好)是真的需要吗?如果你不需要任何动态数据,为什么在DerivedB上调用它? Base在库中,你不能重新定义它。回调也是库管理员,所以你必须有这个 typedef void(Base :: * SEL_SCHEDULE)(float); ,OK。但是为什么不能为B定义 doSomething ,并使指针指向它与DerivedB的实例耦合?您说

Now, as a dirty hacks goes, it's probably not the worst (better than the manual traversing of vtables), but is it really needed? If you don't need any dynamic data, why call it on DerivedB? Base is in the library, you cannot redefine it. Callback is librarian, too, so you have to have this typedef void (Base::*SEL_SCHEDULE)(float);, OK. But why can't you define doSomething for B and make pointer to it to couple with an instance of DerivedB? You say


doSomethingA不访问其任何成员变量或调用
成员函数。它访问静态成员并调用其他静态
函数

doSomethingA does not access any of its member variables or call member functions. It accesses static members and calls other static functions

但是你可以在 doSomethingB 。或者,如果你的回调从对象类型完全解开,并且你需要一个成员函数指针的唯一原因是符合库回调签名,你可以使你的实际回调非成员简单的老函数,行成员 - DoSomething(float dt){ReallyDoSomething(dt);} 的回调构象。

But you can do it in doSomethingB as well. Or, if your callbacks are completely uncoupled from object types, and the only reason you need a member function pointer is the conformance to the library callback signature, you can make your actual callbacks non-member plain old functions, and call them from one-line members-callback conformers like DoSomething(float dt) {ReallyDoSomething(dt);}.

这篇关于C ++:对不同派生类对象的基指针调用(派生的)成员函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆