【问题标题】:C++: Create an anonymous class for event handlersC++:为事件处理程序创建一个匿名类
【发布时间】:2015-09-18 09:14:17
【问题描述】:

免责声明:此描述包含许多 Qt 细节。他们不需要回答这个问题,我只是想给你背景。

我需要对QTextEditfocusInEvent 做出反应。 不幸的是,这不能作为信号使用,这就是为什么我需要子类QTextEdit。由于这是我需要的唯一更改,我想使用匿名子类

像这样:

myTextEdit =new QTextEdit(){
            void focusInEvent(){
     //code here
     }
};

这是我用 Java 编写的代码,它不能用 c++ 编译。 以下所有代码都在自定义 QWidget 的构造函数中。 QTextEdit 包含在此小部件中,应在其构造函数中初始化。

奇怪的是这段代码编译:

class MyTextEdit:protected QTextEdit{
    void focusInEvent();
};
auto myTextEdit=new MyTextEdit();

但是没用,因为我不能将myTextEdit* 的实例分配给指向QTextEdit 的指针。不知何故,多态性失败了。此代码无法编译:

class MyTextEdit:protected QTextEdit{
        void focusInEvent();
    };
QTextEdit* myTextEdit=new MyTextEdit();

编译错误是:

/home/lars/ProgrammierPraktikum/moleculator/implementation/Moleculator/gui_elements/editor.cpp:40: 错误:“QTextEdit”是一个无法访问的基础 'Editor::Editor(std::shared_ptr)::MyTextEdit' QTextEdit* myTextEdit=new MyTextEdit();

实际问题:

如何创建与其超类的指针兼容的匿名子类?

【问题讨论】:

  • 为什么要使用受保护的继承?如果您使用公共继承,那么您的最后一个示例将编译。
  • @ThomasSparber,我需要覆盖该事件。它被标记为虚拟保护。
  • @ThomasSparber,我看了那个问题,但问题不一样。另一个问题询问如何捕获外部变量。我不需要任何外部变量,我只想修改其焦点事件的文本字段。我的问题是匿名类的类型
  • 我不明白你对被标记为虚拟保护的事件发表评论。如果事件是一个类,“标记为虚拟保护”对我来说没有多大意义。顺便说一句,为什么这被标记为 java?

标签: java c++ qt polymorphism anonymous-types


【解决方案1】:

您的子类化尝试

class MyTextEdit:protected QTextEdit{
        void focusInEvent();
    };
QTextEdit* myTextEdit=new MyTextEdit();

几乎没问题。

仅仅因为方法受保护并不意味着您应该使用受保护的方式继承。

  • 一个受保护的方法说:这不是我的界面的一部分。除了我之外,没有人应该可以这样称呼它。我将自己称之为(作为事件处理的一部分)。该方法可以在子类中被覆盖。
  • 继承受保护说:没有人应该知道这种继承,它是一个实现细节,可能对扩展我的类有用。

您想要常规的公共继承。

class MyTextEdit:public QTextEdit{
        void focusInEvent();
    };
QTextEdit* myTextEdit=new MyTextEdit();

现在您说 MyTextEdit 是 QTextEdit 的替代品。 您可能想要添加一个构造函数以将父窗口小部件提供给 MyTextEdit。

c++ 中没有类似 java 的匿名内部类。

【讨论】:

    【解决方案2】:

    您根本不需要子类化。您可以使用可应用于任何事件类型、任何对象的辅助类将事件转换为信号。 EventSignaler 充当一个或多个对象的事件过滤器。当匹配事件到达给定对象时,它会发出eventSignal

    class EventSignaler : public QObject {
       Q_OBJECT
       QMap<QObject*, QSet<int>> m_watch;
       bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {
          auto it = m_watch.find(obj);
          if (it != m_watch.end() && it.value().contains(ev->type()))
             emit eventSignal(EventWrapper(obj, ev));
          return false;
       }
    public:
       EventSignaler(QObject * parent = 0) : QObject(parent) {}
       void watch(QObject * object, QEvent::Type type) {
          auto it = m_watch.find(object);
          if (it == m_watch.end()) {
             it = m_watch.insert(object, QSet<int>() << type);
             object->installEventFilter(this);
             connect(object, &QObject::destroyed, this, [this, object]{
                m_watch.remove(object);
             });
          } else
             it.value().insert(type);
       }
       void unWatch(QObject * object, QEvent::Type type) {
          auto it = m_watch.find(object);
          if (it == m_watch.end()) return;
          it.value().remove(type);
          if (it.value().isEmpty()) m_watch.erase(it);
       }
       Q_SIGNAL void eventSignal(const EventWrapper &);
    };
    

    EventWrapper 是一个帮助类,用于表示事件的类型并安全地携带指向事件的指针。当类被复制时,例如当它通过 queued 连接传递时,原始事件将不再存在,因此包装器将事件指针归零。这是必要的,因为事件通常是不可复制的。

    // https://github.com/KubaO/stackoverflown/tree/master/questions/event-signaler-32648234
    #include <QtWidgets>
    
    struct EventWrapper {
       QPointer<QObject> target;
       QEvent::Type type { QEvent::None };
       QEvent * event { nullptr };
    public:
       EventWrapper() {}
       EventWrapper(QObject * target, QEvent * event) :
          target(target), type(event->type()), event(event) {}
       EventWrapper(const EventWrapper & o) : target(o.target), type(o.type) {}
       EventWrapper(EventWrapper && o) :
          target(o.target), type(o.type), event(o.event) { o.event = nullptr; }
       EventWrapper & operator=(const EventWrapper & o) {
          target = o.target;
          type = o.type;
          event = nullptr;
          return *this;
       }
    };
    Q_DECLARE_METATYPE(EventWrapper)
    

    最后,我们可以通过一个带有标签和行编辑的小示例来演示所有这些。每当您单击标签时,行编辑的文本都会复制到标签中。

    int main(int argc, char ** argv) {
       QApplication app(argc, argv);
       EventSignaler signaler;
       QWidget w;
       QVBoxLayout layout(&w);
       QLabel label("text");
       QLineEdit edit;
       layout.addWidget(&label);
       layout.addWidget(&edit);
       signaler.watch(&label, QEvent::MouseButtonPress);
       QObject::connect(&signaler, &EventSignaler::eventSignal,
                        [&label, &edit]{ label.setText(edit.text()); });
       w.show();
       return app.exec();
    }
    
    #include "main.moc"
    

    【讨论】:

    • 非常有帮助。这就是我实际做的方式。不得不接受另一个问题作为答案,因为这个问题是明确独立的
    猜你喜欢
    • 2010-11-26
    • 1970-01-01
    • 2018-10-06
    • 2013-12-24
    • 2012-05-10
    • 2013-03-04
    • 2011-09-21
    • 1970-01-01
    • 2013-08-15
    相关资源
    最近更新 更多