Базовый класс -> Производный класс и наоборот преобразования в C ++ - PullRequest
1 голос
/ 02 января 2011

У меня есть следующий пример кода:

#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 ++, но я не могу понять, почему я не могу выполнить преобразование из объекта базового класса в производный. Может кто-нибудь предложить мне решение проблемы, которая у меня есть? Спасибо в совете

Ответы [ 5 ]

1 голос
/ 03 января 2011

Ваша функция generateEvent выполняет следующие действия:

  • создает KeyEvent
  • преобразует (преобразует) объект в событие, копируя и разрезая
  • возвращает этот объект события

Затем вы пытаетесь взять эту копию объекта 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; неявно присутствует в функции точки входа.

1 голос
/ 02 января 2011

Ваша проблема основана на том факте, что хотя 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 будет отличаться для каждого производного класса. Метод также может быть чисто виртуальным или нет. Все это зависит от того, что именно вы хотели бы сделать.

В качестве заключительного замечания есть сценарии (и некоторые ответы на этот вопрос), в которых предлагается попытаться «выяснить», какой именно тип события e, а затем привести к этому типу и выполнить какое-то явное действие (например, доступ к key члену KeyEvent), если это определенный тип. Такая обработка называется переключателем типа , и это, как правило, плохая идея. Хотя могут существовать допустимые сценарии, когда требуется переключение типов, гораздо лучше обрабатывать такие случаи так, как они предназначены для обработки на объектно-ориентированном языке (с виртуальными функциями). Сначала научитесь делать вещи по правилам, а потом нарушайте правила.

1 голос
/ 02 января 2011

Что должна делать эта строка:

KeyEvent e = generateEvent();

- это вызов конструктора KeyEvent, который принимает либо Event объект, либо ссылку на него. Однако ваш класс KeyEvent не имеет такого конструктора, поэтому ваш компилятор говорит вам, что он не может сделать KeyEvent из объекта Event ("ошибка: преобразование из 'Event' в не скалярный тип 'KeyEvent' запрошен ").

Вместо этого вы можете использовать этот код:

#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

1 голос
/ 02 января 2011

Просто невозможно автоматически конвертировать из Event в KeyEvent, компилятор не знает, что поместить, например. KeyEvent::key тогда.

Будьте осторожны с предлагаемыми cast решениями, поскольку нет проверки типов, и у вас возникнут проблемы, как только вы получите события разных типов (Event a KeyEvent или MouseEvent? ). Распространенными решениями являются добавление идентификаторов типов или использование виртуальных функций (более безопасных, но иногда менее простых).

1 голос
/ 02 января 2011

Функция generateEvent передает значение Event по значению (а не по указателю или ссылке).Это означает, что объект KeyEvent, который вы пытаетесь передать, будет «разрезан» до объекта Event, а поля key и modifier будут отброшены.

Сообщение об ошибкене самое полезное, но компилятор пытается сказать, что он не может преобразовать значение Event в значение KeyEvent.Преобразование потребовало бы синтеза значений по умолчанию для полей KeyEvent, поскольку исходные поля были вырезаны, когда объект Event был возвращен по значению.

Вы можете избежать этой ошибки, либо динамически распределяя KeyEvent в generateEvent, и generateEvent возвращает Event*, либо generateEvent принимает KeyEvent по ссылке.Используя указатель или ссылку, вы можете избежать проблем с нарезкой объектов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...