1

次のコード例があります。

#include <iostream>
#include <string>

using namespace std;

class Event
{
public:
    string type;
    string source;
};

class KeyEvent : public Event
{
public:
    string key;
    string modifier;
};

class MouseEvent : public Event
{
public:
    string button;
    int x;
    int y;
};

void handleEvent(KeyEvent e)
{
    if(e.key == "ENTER")
        cout << "Hello world! The Enter key was pressed ;)" << endl;
}

Event generateEvent()
{
    KeyEvent e;
    e.type = "KEYBOARD_EVENT";
    e.source = "Keyboard0";
    e.key = "SPACEBAR";
    e.modifier = "none";

    return e;
}

int main()
{
    KeyEvent e = generateEvent();

    return 0;
}

コンパイルできません。G++ は次のようなエラーをスローします。

main.cpp: In function 'int main()':
main.cpp:47:29: error: conversion from 'Event' to non-scalar type 'KeyEvent' requested

エラーが C++ の専門家にとって明らかであることはわかっていますが、基本クラス オブジェクトから派生オブジェクトへの変換を実行できない理由がわかりません。誰かが私が抱えている問題の解決策を教えてもらえますか? アドバイスでthx

4

5 に答える 5

2

関数generateEventは次のことを行います。

  • を作成しますKeyEvent
  • コピーとスライスによってオブジェクトを Event に変換 (アップキャスト) します
  • その Event オブジェクトを返します

次に、そのEventオブジェクトのコピーを取得して、KeyEventもう一度 に入れようとします。

ポリモーフィズムを使用しようとしていますが、実際にはスライスしているだけです。考慮してください (注意してください!) 動的割り当て:

boost::shared_ptr<Event> generateEvent() {
    KeyEvent* e = new KeyEvent;
    e->type = "KEYBOARD_EVENT";
    e->source = "Keyboard0";
    e->key = "SPACEBAR";
    e->modifier = "none";

    return boost::shared_ptr<Event>(static_cast<Event*>(e));
}

int main() {
    boost::shared_ptr<Event> e = generateEvent();
    // you can now use dynamic_cast and/or virtual
    // function calls to treat e as a pointer-to-KeyEvent
}

return 0;また、エントリポイント関数では暗黙的であることに注意してください。

于 2011-01-02T22:36:08.487 に答える
1

この関数は、 (ポインターや参照ではなく)値でgenerateEvent渡します。Eventこれは、KeyEvent渡そうとしているオブジェクトがオブジェクトに「スライス」され、Eventフィールドkeymodifierフィールドが破棄されることを意味します。

エラーメッセージは最も役に立ちませんが、コンパイラが言おうとしているのは、Event値を値に変換できないということKeyEventです。オブジェクトが値によって返されKeyEventたときに元のフィールドがスライスされたため、変換にはフィールドのデフォルト値を合成する必要があります。Event

このエラーを回避するには、inを動的に割り当ててKeyEventgenerateEventgenerateEvent返すEvent*か、参照によってaをgenerateEvent受け入れKeyEventます。ポインタまたは参照を使用することで、オブジェクトのスライスの問題を回避できます。

于 2011-01-02T15:43:12.167 に答える
1

この行が行うべきこと:

KeyEvent e = generateEvent();

オブジェクトまたはオブジェクトへの参照のKeyEventいずれかを取るコンストラクターを呼び出します。Eventただし、クラスにはそのようなコンストラクターがないため、コンパイラーは、オブジェクトKeyEventを作成できないことを通知しています (「エラー: 'Event' から非スカラー型 'KeyEvent' への変換が要求されました」)。KeyEventEvent

代わりに使用できるのは、次のコードです。

#include <iostream>
#include <memory>
#include <string>

using namespace std;

class Event
{
public:
    string type;
    string source;

    virtual ~Event() { }
};

class KeyEvent : public Event
{
public:
    string key;
    string modifier;

    virtual ~KeyEvent() { }
};

class MouseEvent : public Event
{
public:
    string button;
    int x;
    int y;

    virtual ~MouseEvent() { }
};

void handleEvent(const KeyEvent& e)
{
    if(e.key == "SPACEBAR")
        cout << "Hello world! The Space key was pressed ;)" << endl;
}

auto_ptr<Event> generateEvent()
{
    auto_ptr<KeyEvent> ret(new KeyEvent);
    ret->type = "KEYBOARD_EVENT";
    ret->source = "Keyboard0";
    ret->key = "SPACEBAR";
    ret->modifier = "none";

    return auto_ptr<Event>(ret.release());
}

int main()
{
    auto_ptr<Event> pEvent = generateEvent();
    KeyEvent *pKeyEvent = dynamic_cast<KeyEvent*>(pEvent.get());
    if (pKeyEvent) {
        handleEvent(*pKeyEvent);
    }

    return 0;
}

http://codepad.org/DcBi7jxq

于 2011-01-02T15:52:29.540 に答える
1

Eventからに自動的に変換することは不可能です。KeyEventコンパイラは、たとえば何を入れるかを知りませんKeyEvent::key

提案された解決策には注意してください。型チェックがなく、さまざまな型のイベント ( aまたは a ?)castを受け取るとすぐに問題が発生します。一般的な解決策は、タイプ ID を追加するか、仮想関数を使用することです (より安全ですが、場合によっては単純ではありません)。EventKeyEventMouseEvent

于 2011-01-02T15:53:46.890 に答える
1

generateEventあなたが抱えている問題は、 が実際には を作成しているという事実に基づいていますがKeyEvent、これはプログラマー (あなた) だけが知っていることです。コンパイラが知っているのは、 が をgenerateEvent返すということですがEvent、これは一般的に実際には ではありませんKeyEvent。したがって、コンパイラは、正式に (関数定義が示すように) ではないものを として扱っていると不平を言いKeyEventますKeyEvent

おそらく、内部でやりたいことはmain、イベントが実際にKeyEvent. これは一般的なシナリオであり、実行しようとしていることに問題はありません。別の方法で行う必要があります。

この場合、私たちがやりたいことは、イベントに対して「アクション X を実行する」ことです。ここで、「アクション X」は、イベントが であるKeyEventか他のものであるかによって異なります。それを行う方法は、次のように仮想関数を使用することです。

class Event
{
public:
    string type;
    string source;

    virtual void PerformActionX();
};

その後:

int main()
{
    Event e = generateEvent();
    e.PerformActionX();

    return 0;
}

の実装はPerformActionX、派生クラスごとに異なります。メソッドは、純粋な仮想であってもなくてもかまいません。これはすべて、正確に何をしたいかによって異なります。

最後に、イベントのタイプを正確に「発見」し、そのタイプにキャストして明示的なアクションを実行することを提案するシナリオ (およびこの質問に対するいくつかの回答) があります(たとえば、 a のメンバーeへのアクセスなど) 。特定のタイプの場合。この種の処理はtype switchと呼ばれ、一般的には悪い考えです。型の切り替えが必要となる有効なシナリオもあるかもしれませんが、そのような場合は、オブジェクト指向言語 (仮想関数を使用) で処理されるように処理することをお勧めします。まず、ルールに従って物事を行う方法を学び、ルールを破ることは後で行います。keyKeyEvent

于 2011-01-02T16:15:29.270 に答える