Question

J'ai le code exemple suivant:

#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;
}

Je ne peux pas le compiler, G ++ renvoie une erreur de type:

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

Je sais que l'erreur est évidente pour le gourou de C ++, mais je ne comprends pas pourquoi je ne peux pas faire la conversion de l'objet de classe de base à un dérivé. Quelqu'un peut-il me suggérer la solution du problème que j'ai? Thx dans le conseil

Était-ce utile?

La solution

Votre fonction generateEvent effectue les opérations suivantes:

  • crée un KeyEvent
  • convertit (upcasts) l'objet dans un événement en copiant et découpage
  • renvoie cet objet événement

Vous essayez de prendre cette copie de l'objet Event et le mettre dans un KeyEvent nouveau.

Vous essayez d'utiliser le polymorphisme, mais sont en fait juste de trancher. Tenez compte (avec précaution!) L'allocation dynamique:

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
}

Notez également que return 0; est implicite dans la entrypoint.

Autres conseils

La fonction generateEvent passe un Event par valeur (plutôt que par pointeur ou de référence). Cela signifie que l'objet KeyEvent que vous essayez de passer sera « découpé en tranches » jusqu'à un objet Event, et les champs de key et modifier sera mis au rebut.

Le message d'erreur est pas le plus utile, mais ce que le compilateur essaie de dire est qu'il ne peut pas convertir une valeur Event à une valeur de KeyEvent. La conversion nécessiterait la synthèse des valeurs par défaut pour les champs de KeyEvent, car les champs d'origine ont été coupés en tranches loin lorsque l'objet Event a été retourné par valeur.

Vous pouvez éviter cette erreur, soit une allocation dynamique KeyEvent en generateEvent, et ayant generateEvent retourner un Event*, ou en ayant generateEvent accepter un KeyEvent par référence. En utilisant un pointeur ou une référence, vous pouvez éviter le problème de découpage de l'objet.

Il est tout simplement impossible de convertir automatiquement Event à KeyEvent, le compilateur ne sait pas quoi mettre en exemple KeyEvent::key alors.

Soyez prudent avec les solutions proposées cast, car il n'y a pas de vérification de type, et vous aurez des problèmes dès que vous recevez des événements de différents types (est Event un KeyEvent ou MouseEvent?). des solutions communes pour ajouter ou de type ids d'utiliser des fonctions virtuelles (plus sûr, mais parfois de façon moins directe).

Qu'est-ce que cette ligne est censé faire:

KeyEvent e = generateEvent();

est appeler un constructeur de KeyEvent qui prend soit un objet Event ou une référence à une. Votre classe KeyEvent, cependant, ne dispose pas d'un tel constructeur, de sorte que votre compilateur vous dit qu'il ne peut pas faire un KeyEvent sur un objet Event ( "Erreur: la conversion de « événement » type non scalaire « KeyEvent » demandé « ).

Qu'est-ce que vous pouvez utiliser est plutôt ce code:

#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

Le problème que vous avez est basé sur le fait que tout generateEvent a en fait créer un KeyEvent, c'est quelque chose que seul le programmeur (vous) connaît. Qu'est-ce que le compilateur sait est que revient generateEvent un de Event, qui, dans le cas général est pas en fait une KeyEvent. Par conséquent, le compilateur se plaint que vous traitez quelque chose qui formellement (comme les états de définition de fonction) n'est pas un KeyEvent comme KeyEvent.

Très probablement, ce que vous voulez faire à l'intérieur main est d'effectuer une action si l'événement est en fait un KeyEvent. Ce scénario est commun, et il n'y a rien de mal à ce que vous essayez de faire. Vous avez juste besoin de le faire différemment.

Dans ce cas, ce que nous voulons faire est de « faire une action X » sur l'événement, où « l'action X » est quelque chose de différent selon que l'événement est un KeyEvent ou autre chose. La façon de le faire est avec des fonctions virtuelles, comme suit:

class Event
{
public:
    string type;
    string source;

    virtual void PerformActionX();
};

Et puis:

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

    return 0;
}

La mise en œuvre de PerformActionX serait différent pour chaque classe dérivée. La méthode pourrait aussi être virtuelle pure ou non. Tout cela dépend de ce que vous voulez exactement faire.

Comme note finale, il y a des scénarios (et quelques réponses à cette question) qui suggère d'essayer de « découvrir » exactement quel type de e d'événement est, puis coulée de ce type et d'effectuer une action explicite (comme par exemple l'accès à la élément de key d'un KeyEvent) si elle est de type spécifique. Ce genre de manipulation est appelé commutateur de type , et il est généralement une mauvaise idée. Bien qu'il puisse y avoir des scénarios valides où un commutateur de type est appelé, il est une bien meilleure idée de traiter de tels cas, la façon dont ils sont destinés à être traités dans un langage orienté objet (avec des fonctions virtuelles). d'abord apprendre comment faire les choses selon les règles, et laisser enfreindre les règles pour plus tard.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top