【问题标题】:Does Qt support virtual pure slots?Qt 是否支持虚拟纯插槽?
【发布时间】:2011-03-01 04:55:09
【问题描述】:

我在Qt 中的GUI 项目有很多“配置页面”类,它们都直接继承自QWidget

最近,我意识到所有这些类共享 2 个公共插槽(loadSettings()saveSettings())。

关于这个,我有两个问题:

  • 用这两个插槽作为虚拟纯方法编写一个中间基抽象类(我们将其命名为BaseConfigurationPage)有意义吗? (每个可能的配置页面总是有这两种方法,所以我会说“是”)
  • 在对代码进行重大更改之前(如果必须):Qt 是否支持虚拟纯插槽?有什么需要注意的吗?

这是一个描述所有内容的代码示例:

class BaseConfigurationPage : public QWidget
{
  // Some constructor and other methods, irrelevant here.

  public slots:

    virtual void loadSettings() = 0;
    virtual void saveSettings() = 0;
};

class GeneralConfigurationPage : public BaseConfigurationPage
{
  // Some constructor and other methods, irrelevant here.

  public slots:

    void loadSettings();
    void saveSettings();
};

【问题讨论】:

    标签: c++ qt inheritance signals-slots


    【解决方案1】:

    是的,就像常规的 c++ 纯虚方法一样。 MOC 生成的代码确实调用了纯虚拟槽,但没关系,因为无论如何都无法实例化基类...

    同样,就像常规的 c++ 纯虚方法一样,在为方法提供实现之前,无法实例化该类。

    有一件事:在子类中,您实际上不需要将覆盖的方法标记为槽。首先,它们已经被实现为基类中的插槽。其次,您只是在为 MOC 和编译器创建更多工作,因为您添加了(微小的)更多代码。微不足道,但无论如何。

    所以,去吧..

    【讨论】:

    • 感谢您的准确回答!我会尽快测试这个;)
    • 从子类中删除槽规范会阻止 moc 调用子类和基类! - 谢谢老兄!
    • 至少在 Qt 5 中,如果您使用的是 obj-ptr, member-func-ptr, obj-ptr, member-func-ptr 版本的 connect,则 none 您的插槽需要声明 i> 这样。
    • 必须添加一些非常奇怪的行为:当您将覆盖的方法标记为子类标头中的插槽时,即使与它们的连接为 0,插槽也会一直被调用。去图!!!
    【解决方案2】:

    仅 BaseConfigurationPage 中的插槽

    class BaseConfigurationPage : public QWidget
    {
      // Some constructor and other methods, irrelevant here.
    
      public slots:
    
        virtual void loadSettings() = 0;
        virtual void saveSettings() = 0;
    };
    
    class GeneralConfigurationPage : public BaseConfigurationPage
    {
      // Some constructor and other methods, irrelevant here.
    
        void loadSettings();
        void saveSettings();
    };
    

    【讨论】:

    • 我也在做同样的事情,但我得到“插槽名称”覆盖成员函数但未标记为“覆盖”
    • 是的,loadSettingssaveSettings 应该在 GeneralConfigurationPage 中声明为 override,以确保它们实际上覆盖了某些内容。
    【解决方案3】:

    其他人已经解释了虚拟、继承和插槽的机制,但我想我会回到这部分或问题:

    用这两个插槽作为虚拟纯方法编写一个中间基础抽象类是否有意义?

    我会说,只有在你有使用该抽象的情况下才有意义,或者换句话说,如果你的代码在一个或多个BaseConfigurationPages 上运行而不关心实际类型。

    假设您的对话代码非常灵活,并且包含std::vector<BaseConfigurationPage*> m_pages。您的加载代码可能如下所示。在这种情况下,抽象基类就有意义了。

    void MyWizard::loadSettings()
    {
        for(auto * page : m_pages)
        {
            page->loadSettings();
        }
    }
    

    但是,另一方面,假设您的对话框实际上是相当静态的并且具有IntroPage * m_introPage; CustomerPage * m_customerPage; ProductPage * m_productPage;。您的加载代码可能如下所示。

    void MyWizard::loadSettings()
    {
        m_introPage->loadSettings();
        m_customerPage->loadSettings();
        m_productPage->loadSettings();
    }
    

    在这种情况下,BaseConfigurationPage 绝对不会为您带来任何好处。它增加了复杂性并增加了代码行数,但没有增加表达能力,也不能保证正确性。

    没有更多上下文,这两种选择都不一定更好。

    作为学生或新程序员,我们通常被教导识别和抽象出重复,但这实际上是一种简化。我们应该寻找有价值的抽象。重复可能暗示需要抽象,或者它可能只是有时实现具有模式的标志。仅仅因为注意到一种模式而引入抽象是一个很常见的设计陷阱。

    Dolphin 的设计和Shark 的设计看起来很相似。人们可能很想插入一个TorpedoShapedSwimmer 基类来捕获这些共性,但是这种抽象是否提供了价值,或者它实际上可能会在稍后实现breathe(), 'lactate()orgrowSkeleton( )`?

    我意识到这是一个关于基于一些简单示例代码的子问题的长篇大论,但我最近在工作中多次遇到这种模式:基类只捕获重复而不增加价值,但进入未来变化的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-30
      • 1970-01-01
      • 2013-09-08
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多