Qt
中的我的GUI项目有很多“配置页面”类,它们都直接从QWidget
继承。
最近,我意识到所有这些类共享2个公共位置(loadSettings()
和saveSettings()
)。
关于这一点,我有两个问题:
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.
public slots:
void loadSettings();
void saveSettings();
};
答案 0 :(得分:148)
是的,就像常规的c ++纯虚方法一样。 MOC生成的代码确实调用了纯虚拟插槽,但是没关系,因为基类无论如何都无法实例化......
同样,就像常规的c ++纯虚方法一样,在给出实现方法之前,不能实例化类。
一件事:在子类中,您实际上不需要将重写方法标记为插槽。首先,它们已经实现为基类中的插槽。其次,您只是为MOC和编译器创建了更多的工作,因为您要添加(微小)更多的代码。琐碎,但无论如何。
所以,去吧..
答案 1 :(得分:0)
只有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();
};
答案 2 :(得分:0)
其他人已经解释了虚拟、继承和插槽的机制,但我想我会回到这部分或问题:
<块引用>用这两个插槽作为虚拟纯方法编写一个中间基础抽象类有意义吗?
我会说只有在您使用该抽象时才有意义,或者换句话说,如果您的代码在一个或多个 BaseConfigurationPage
上运行而不关心关于实际类型。
假设您的对话代码非常灵活并且包含 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(){ 时实际上会增加不必要的摩擦{1}}growSkeleton()`?
我意识到这是关于基于一些简单示例代码的子问题的长篇大论,但我最近在工作中多次遇到这种模式:只捕获重复而不增加价值的基类,但进入未来变化的方式。