0

基本クラスのポインターでメンバー関数 (ポインター) を呼び出すことは「安全」(および/または移植可能) ですが、指されているオブジェクトはインスタンスの異なる派生クラスです。メンバー関数は、派生クラスのメンバー変数または関数にはアクセスしません。

/* 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 用のコンパイラでこのコードをまだテストしていません)。

必要に応じてさらに具体的に: 私はcocos2d-xベースのプロジェクトを構築しています。BaseでありCCObjectDerivedADerivedBサブクラスですCCLayer

階層はDerivedAand DerivedB<< CCLayer<CCNodeですCCObject。それらは、相互に排他的な時間に表示される/生きているゲーム シーンです。

DerivedA呼び出し元オブジェクトをパラメーターとして受け取り、次のようなものを使用して再生を開始し、ゆっくりとフェードインするように別のセレクター( ) をスケジュールする、音楽の再生をセットアップする別の静的関数があります。CCNodedoSomethingA

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

schedule_selectorCスタイルのキャストが行うものです。doSomethingAメンバー変数にアクセスしたり、メンバー関数を呼び出したりしません。静的メンバーにアクセスし、次のような他の静的関数を呼び出します

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

doSomethingA実行時の呼び出しはCCTimer::updateで行われます。

ハックの主な目的は、コードの重複を避け、ライブラリのコールバック シグネチャ (タイマー/スケジューラ システム) に準拠することです。

4

3 に答える 3

1

これはBadなのかな(TM)

共通の基本クラスで宣言された仮想関数のオーバーライドでない限り、それは確かにそうです。

そうであれば、危険なキャストは必要ありません。から直接初期化するだけ&Base::DoSomethingAです。

そうでない場合は、邪悪な C キャスト (ここではreinterpret_cast変装しています) を使用すると、そのメンバー関数を持たない型にポインターを適用できます。フランケンシュタインの忌まわしきものは絶対に何でもできると呼んでいます。関数が実際にオブジェクトに触れていない場合は、悪影響が見られない可能性が高くなります。しかし、あなたはまだしっかりと未定義の動作をしています。

于 2013-04-17T11:54:35.823 に答える