基类-> C++ 中的派生类和反之亦然的转换;

发布于 2024-10-10 08:04:47 字数 971 浏览 0 评论 0原文

我有以下示例代码:

#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++ 大师来说是显而易见的,但我不明白为什么我不能从基类对象进行转换到派生一。有人可以建议我解决我遇到的问题吗?谢谢建议

I have the following example code:

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

I can't compile it, G++ throws an error of kind:

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

I know that the error is obvious for C++ guru's, but I can't understand why I can't do the conversion from base class object to derived one. Can someone suggest me the solution of the problem that I have? Thx in advice

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

青春如此纠结 2024-10-17 08:04:47

您的函数 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; 隐含在入口点函数中。

Your function generateEvent does the following:

  • creates a KeyEvent
  • converts (upcasts) the object into an Event by copying and slicing
  • returns that Event object

You then try to take that Event object copy and put it into a KeyEvent again.

You are trying to use polymorphism but are actually just slicing. Consider (with caution!) dynamic allocation:

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
}

Also note that return 0; is implicit in the entrypoint function.

七婞 2024-10-17 08:04:47

generateEvent 函数通过值(而不是通过指针或引用)传递Event。这意味着您尝试传递的 KeyEvent 对象将被“切片”为 Event 对象,并且 key修饰符字段将被丢弃。

错误消息并不是最有帮助的,但编译器试图说明的是它无法将 Event 值转换为 KeyEvent 值。转换需要合成 KeyEvent 字段的默认值,因为当按值返回 Event 对象时,原始字段被切掉。

您可以通过在 generateEvent 中动态分配 KeyEvent 并让 generateEvent 返回 Event* 来避免此错误,或者让 generateEvent 通过引用接受 KeyEvent 。通过使用指针或引用,可以避免对象切片问题。

The generateEvent function passes an Event by value (rather than by pointer or reference). This means that the KeyEvent object you are trying to pass will be "sliced" down to an Event object, and the key and modifier fields will be discarded.

The error message is not the most helpful, but what the compiler is trying to say is that it can't convert an Event value to a KeyEvent value. The conversion would require synthesizing default values for the KeyEvent fields, because the original fields were sliced away when the Event object was returned by value.

You can avoid this error by either dynamically allocating a KeyEvent in generateEvent, and having generateEvent return an Event*, or by having generateEvent accept a KeyEvent by reference. By using a pointer or reference, you can avoid the object slicing problem.

深巷少女 2024-10-17 08:04:47

自动从 Event 转换为 KeyEvent 是不可能的,编译器不知道要放入什么内容,例如 KeyEvent::key

请小心建议的 cast 解决方案,因为没有类型检查,一旦收到不同类型的事件(Event>KeyEvent 还是 MouseEvent?)。常见的解决方案是添加类型 ID 或使用虚拟函数(更安全,但有时不太简单)。

It's just impossible to automatically convert from Event to KeyEvent, the compiler doesn't know what to put into e.g. KeyEvent::key then.

Be careful with the proposed cast solutions, as there is no type checking, and you will have problems as soon as you receive events of different types (is an Event a KeyEvent or a MouseEvent?). Common solutions are to add type ids or to use virtual functions (safer, but sometimes way less straightforward).

烟沫凡尘 2024-10-17 08:04:47

这一行的作用是:

KeyEvent e = generateEvent();

调用 KeyEvent 的构造函数,该构造函数接受 Event 对象或对事件对象的引用。但是,您的 KeyEvent 类没有这样的构造函数,因此编译器告诉您它无法从 Event 中创建 KeyEvent代码>对象(“错误:请求从‘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

What this line is supposed to do:

KeyEvent e = generateEvent();

is call a constructor of KeyEvent that takes either an Event object or a reference to one. Your KeyEvent class, however, does not have such a constructor, so your compiler is telling you that it can't make a KeyEvent out of an Event object ("error: conversion from 'Event' to non-scalar type 'KeyEvent' requested").

What you could use instead is this 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

飞烟轻若梦 2024-10-17 08:04:47

您遇到的问题是基于这样一个事实:虽然 generateEvent 实际上创建了一个 KeyEvent,但只有程序员(您)知道这一点。 编译器知道的是 generateEvent 返回一个 Event,在一般情况下,它实际上不是代码>KeyEvent。因此,编译器会抱怨您正在将形式上(如函数定义所述)不是 KeyEvent 的东西视为 KeyEvent

最有可能的是,如果事件实际上是一个 KeyEvent,您想要在 main 中执行的操作是执行某些操作。这是一种常见的情况,您尝试做的事情没有任何问题。你只需要以不同的方式去做。

在本例中,我们要做的是对事件“执行操作 X”,其中“操作 X”根据事件是 KeyEvent 还是其他事件而有所不同。实现此目的的方法是使用虚函数,如下所示:

class Event
{
public:
    string type;
    string source;

    virtual void PerformActionX();
};

然后:

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

    return 0;
}

对于每个派生类,PerformActionX 的实现将有所不同。该方法也可以是纯虚拟的或不是纯虚拟的。所有这一切都取决于您到底想做什么。

最后一点,有些场景(以及这个问题的一些答案)建议尝试“发现”事件 e 到底是什么类型,然后转换为该类型并执行一些显式操作(例如访问 KeyEventkey 成员)(如果它是特定类型)。这种处理称为类型切换,这通常是一个坏主意。虽然可能存在需要类型切换的有效场景,但最好以面向对象语言(使用虚拟函数)处理此类情况的方式来处理它们。首先学会如何按规则做事,然后再去打破规则。

The problem you have is based on the fact that while generateEvent does in fact create a KeyEvent, this is something that only the programmer (you) knows. What the compiler knows is that generateEvent returns an Event, which in the general case is not in fact a KeyEvent. Therefore, the compiler complains that you are treating something that formally (as the function definition states) is not a KeyEvent as a KeyEvent.

Most probably, what you want to do inside main is to perform some action if the event is in fact a KeyEvent. This is a common scenario, and there is nothing wrong with what you are trying to do. You just need to do it differently.

In this case, what we want to do is "perform action X" on the event, where "action X" is something different depending on whether the event is a KeyEvent or something else. The way to do it is with virtual functions, like so:

class Event
{
public:
    string type;
    string source;

    virtual void PerformActionX();
};

And then:

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

    return 0;
}

The implementation of PerformActionX would be different for each derived class. The method could also be pure virtual or not. All this is dependent on what exactly you would want to do.

As a final note, there are scenarios (and some answers to this question) which suggest trying to "discover" exactly what type of event e is, and then casting to that type and performing some explicit action (like e.g. accessing the key member of a KeyEvent) if it is a specific type. This kind of handling is called a type switch, and it's generally a bad idea. While there might be valid scenarios where a type switch is called for, it is a much better idea to handle such cases the way they are meant to be handled in an object-oriented language (with virtual functions). First learn how to do things by the rules, and leave breaking the rules for later.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文